Merge branch 'master' into gonesolong

Conflicts:
	src/CMakeLists.txt
	src/b_think.cpp
	src/g_doom/a_doomweaps.cpp
	src/g_hexen/a_clericstaff.cpp
	src/g_hexen/a_fighterplayer.cpp
	src/namedef.h
	src/p_enemy.cpp
	src/p_local.h
	src/p_mobj.cpp
	src/p_teleport.cpp
	src/sc_man_tokens.h
	src/thingdef/thingdef_codeptr.cpp
	src/thingdef/thingdef_function.cpp
	src/thingdef/thingdef_parse.cpp
	wadsrc/static/actors/actor.txt
	wadsrc/static/actors/constants.txt
	wadsrc/static/actors/shared/inventory.txt

- Added register reuse to VMFunctionBuilder for FxPick's code emitter.
- Note to self: Need to reimplement IsPointerEqual and CheckClass, which
  were added to thingdef_function.cpp over the past year, as this file no
  longer exists in this branch.
This commit is contained in:
Randy Heit 2014-12-21 20:43:24 -06:00
commit b5e4153c78
182 changed files with 23029 additions and 8734 deletions

View File

@ -3,6 +3,7 @@ cmake_minimum_required( VERSION 2.4 )
make_release_only()
include( CheckFunctionExists )
include( CheckCXXCompilerFlag )
# DUMB is much slower in a Debug build than a Release build, so we force a Release
# build here, since we're not maintaining DUMB, only using it.
@ -104,5 +105,9 @@ add_library( dumb
target_link_libraries( dumb )
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
set_source_files_properties( src/it/filter.cpp PROPERTIES COMPILE_FLAGS -msse )
CHECK_CXX_COMPILER_FLAG( -msse DUMB_CAN_USE_SSE )
if( DUMB_CAN_USE_SSE )
set_source_files_properties( src/it/filter.cpp PROPERTIES COMPILE_FLAGS -msse )
endif( DUMB_CAN_USE_SSE )
endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )

View File

@ -70,9 +70,9 @@ static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d)
*c = c2;
*d = d2;
#elif __PIC__
#elif defined __PIC__ && defined __i386__
/* GCC or Clang WITH position-independent code generation */
/* GCC or Clang WITH position-independent code generation, i386 only */
__asm__ __volatile__ (
"xchgl %%ebx, %1\n"
@ -86,7 +86,7 @@ static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d)
#else
/* GCC or Clang WITHOUT position-independent code generation */
/* GCC or Clang WITHOUT position-independent code generation, or x86_64 */
__asm__ __volatile__ (
"cpuid"

View File

@ -1,6 +1,7 @@
cmake_minimum_required( VERSION 2.4 )
add_library( output_sdl MODULE output_sdl.c )
include_directories( ${FMOD_INCLUDE_DIR} ${SDL_INCLUDE_DIR} )
target_link_libraries( output_sdl SDL )
FILE( WRITE ${CMAKE_CURRENT_BINARY_DIR}/link-make "if [ ! -e ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so ]; then ln -sf output_sdl/liboutput_sdl.so ${ZDOOM_OUTPUT_DIR}/liboutput_sdl.so; fi" )
add_custom_command( TARGET output_sdl POST_BUILD

View File

@ -92,30 +92,33 @@ Note: All <bool> fields default to false unless mentioned otherwise.
linedef
{
alpha = <float>; // Translucency of this line, default is 1.0
renderstyle = <string>; // Render style, can be "translucent" or "add",
// default is "translucent".
playeruseback = <bool>; // New SPAC flag, true = player can use from back side.
anycross = <bool>; // New SPAC flag, true = any non-projectile
// crossing will trigger this line
monsteractivate = <bool>; // Monsters can trigger this line.
// For compatibility only because this flag's
// semantics can not be fully reproduced with
// explicit trigger flags.
blockplayers = <bool>; // Line blocks players' movement.
blockeverything = <bool>; // Line blocks everything.
firstsideonly = <bool>; // Line can only be triggered from the front side.
zoneboundary = <bool>; // Line is a boundary for sound reverb zones.
clipmidtex = <bool>; // Line's mid textures are clipped to floor and ceiling.
wrapmidtex = <bool>; // Line's mid textures are wrapped.
midtex3d = <bool>; // Actors can walk on mid texture.
checkswitchrange = <bool>;// Switches can only be activated when vertically reachable.
blockprojectiles = <bool>;// Line blocks all projectiles
blockuse = <bool>; // Line blocks all use actions
blocksight = <bool>; // Line blocks monster line of sight
blockhitscan = <bool>; // Line blocks hitscan attacks
locknumber = <int>; // Line special is locked
arg0str = <string>; // Alternate string-based version of arg0
alpha = <float>; // Translucency of this line, default is 1.0
renderstyle = <string>; // Render style, can be "translucent" or "add",
// default is "translucent".
playeruseback = <bool> ; // New SPAC flag, true = player can use from back side.
anycross = <bool>; // New SPAC flag, true = any non-projectile
// crossing will trigger this line
monsteractivate = <bool>; // Monsters can trigger this line.
// For compatibility only because this flag's
// semantics can not be fully reproduced with
// explicit trigger flags.
blockplayers = <bool>; // Line blocks players' movement.
blockeverything = <bool>; // Line blocks everything.
firstsideonly = <bool>; // Line can only be triggered from the front side.
zoneboundary = <bool>; // Line is a boundary for sound reverb zones.
clipmidtex = <bool>; // Line's mid textures are clipped to floor and ceiling.
wrapmidtex = <bool>; // Line's mid textures are wrapped.
midtex3d = <bool>; // Actors can walk on mid texture.
midtex3dimpassible = <bool>;// Used in conjuction with midtex3d - causes the mid
// texture to behave like an impassible line (projectiles
// pass through it).
checkswitchrange = <bool>; // Switches can only be activated when vertically reachable.
blockprojectiles = <bool>; // Line blocks all projectiles
blockuse = <bool>; // Line blocks all use actions
blocksight = <bool>; // Line blocks monster line of sight
blockhitscan = <bool>; // Line blocks hitscan attacks
locknumber = <int>; // Line special is locked
arg0str = <string>; // Alternate string-based version of arg0
transparent = <bool>; // true = line is a Strife transparent line (alpha 0.25)

File diff suppressed because it is too large Load Diff

View File

@ -102,6 +102,7 @@ DEFINE_SPECIAL(Scroll_Texture_Left, 100, -1, -1, 2)
DEFINE_SPECIAL(Scroll_Texture_Right, 101, -1, -1, 2)
DEFINE_SPECIAL(Scroll_Texture_Up, 102, -1, -1, 2)
DEFINE_SPECIAL(Scroll_Texture_Down, 103, -1, -1, 2)
DEFINE_SPECIAL(Ceiling_CrushAndRaiseSilentDist, 104, 3, 5, 5)
DEFINE_SPECIAL(Light_ForceLightning, 109, 1, 1, 1)
DEFINE_SPECIAL(Light_RaiseByValue, 110, 2, 2, 2)

View File

@ -343,6 +343,20 @@ enum
MF7_ALWAYSTELEFRAG = 0x00000004, // will unconditionally be telefragged when in the way. Overrides all other settings.
MF7_HANDLENODELAY = 0x00000008, // respect NoDelay state flag
MF7_WEAPONSPAWN = 0x00000010, // subject to DF_NO_COOP_WEAPON_SPAWN dmflag
MF7_HARMFRIENDS = 0x00000020, // is allowed to harm friendly monsters.
MF7_BUDDHA = 0x00000040, // Behaves just like the buddha cheat.
MF7_FOILBUDDHA = 0x00000080, // Similar to FOILINVUL, foils buddha mode.
MF7_DONTTHRUST = 0x00000100, // Thrusting functions do not take, and do not give thrust (damage) to actors with this flag.
MF7_ALLOWPAIN = 0x00000200, // Invulnerable or immune (via damagefactors) actors can still react to taking damage even if they don't.
MF7_CAUSEPAIN = 0x00000400, // Damage sources with this flag can cause similar effects like ALLOWPAIN.
MF7_THRUREFLECT = 0x00000800, // Actors who are reflective cause the missiles to not slow down or change angles.
MF7_MIRRORREFLECT = 0x00001000, // Actor is turned directly 180 degrees around when reflected.
MF7_AIMREFLECT = 0x00002000, // Actor is directly reflected straight back at the one who fired the projectile.
MF7_HITTARGET = 0x00004000, // The actor the projectile dies on is set to target, provided it's targetable anyway.
MF7_HITMASTER = 0x00008000, // Same as HITTARGET, except it's master instead of target.
MF7_HITTRACER = 0x00010000, // Same as HITTARGET, but for tracer.
// --- mobj.renderflags ---
@ -679,6 +693,9 @@ public:
// Transforms the actor into a finely-ground paste
virtual bool Grind(bool items);
// Get this actor's team
int GetTeam();
// Is the other actor on my team?
bool IsTeammate (AActor *other);
@ -820,13 +837,14 @@ public:
DWORD flags4; // [RH] Even more flags!
DWORD flags5; // OMG! We need another one.
DWORD flags6; // Shit! Where did all the flags go?
DWORD flags7; //
DWORD flags7; // WHO WANTS TO BET ON 8!?
// [BB] If 0, everybody can see the actor, if > 0, only members of team (VisibleToTeam-1) can see it.
DWORD VisibleToTeam;
int special1; // Special info
int special2; // Special info
int weaponspecial; // Special info for weapons.
int health;
BYTE movedir; // 0-7
SBYTE visdir;
@ -906,9 +924,6 @@ public:
TObjPtr<AInventory> Inventory; // [RH] This actor's inventory
DWORD InventoryID; // A unique ID to keep track of inventory items
//Added by MC:
SDWORD id; // Player ID (for items, # in list.)
BYTE smokecounter;
BYTE FloatBobPhase;
BYTE FriendPlayer; // [RH] Player # + 1 this friendly monster works for (so 0 is no player, 1 is player 0, etc)
@ -934,9 +949,12 @@ public:
FNameNoInit DamageType;
FNameNoInit DamageTypeReceived;
fixed_t DamageFactor;
fixed_t DamageMultiply;
FNameNoInit PainType;
FNameNoInit DeathType;
PClassActor *TeleFogSourceType;
PClassActor *TeleFogDestType;
FState *SpawnState;
FState *SeeState;

View File

@ -56,6 +56,13 @@ AActor *COPY_AAPTR(AActor *origin, int selector)
case AAPTR_TRACER: return origin->tracer;
case AAPTR_FRIENDPLAYER:
return origin->FriendPlayer ? AAPTR_RESOLVE_PLAYERNUM(origin->FriendPlayer - 1) : NULL;
case AAPTR_GET_LINETARGET:
{
AActor *gettarget = NULL;
P_BulletSlope(origin, &gettarget);
return gettarget;
}
}
}

View File

@ -36,12 +36,13 @@ enum AAPTR
AAPTR_PLAYER8 = 0x2000,
AAPTR_FRIENDPLAYER = 0x4000,
AAPTR_GET_LINETARGET = 0x8000,
AAPTR_PLAYER_SELECTORS =
AAPTR_PLAYER_GETTARGET|AAPTR_PLAYER_GETCONVERSATION,
AAPTR_GENERAL_SELECTORS =
AAPTR_TARGET|AAPTR_MASTER|AAPTR_TRACER|AAPTR_FRIENDPLAYER,
AAPTR_TARGET|AAPTR_MASTER|AAPTR_TRACER|AAPTR_FRIENDPLAYER|AAPTR_GET_LINETARGET,
AAPTR_STATIC_SELECTORS =
AAPTR_PLAYER1|AAPTR_PLAYER2|AAPTR_PLAYER3|AAPTR_PLAYER4|

View File

@ -908,8 +908,8 @@ void AM_StaticInit()
if (gameinfo.mMapArrow.IsNotEmpty()) AM_ParseArrow(MapArrow, gameinfo.mMapArrow);
if (gameinfo.mCheatMapArrow.IsNotEmpty()) AM_ParseArrow(CheatMapArrow, gameinfo.mCheatMapArrow);
AM_ParseArrow(CheatKey, "maparrows/key.txt");
AM_ParseArrow(EasyKey, "maparrows/ravenkey.txt");
AM_ParseArrow(CheatKey, gameinfo.mCheatKey);
AM_ParseArrow(EasyKey, gameinfo.mEasyKey);
if (MapArrow.Size() == 0) I_FatalError("No automap arrow defined");
char namebuf[9];

View File

@ -1,7 +1,7 @@
// Cajun bot console commands.
// Cajun bot
//
// [RH] Moved out of d_netcmd.c (in Cajun source), because they don't really
// belong there.
// [RH] Moved console commands out of d_netcmd.c (in Cajun source), because
// they don't really belong there.
#include "c_cvars.h"
#include "c_dispatch.h"
@ -14,6 +14,102 @@
#include "d_net.h"
#include "farchive.h"
IMPLEMENT_POINTY_CLASS(DBot)
DECLARE_POINTER(dest)
DECLARE_POINTER(prev)
DECLARE_POINTER(enemy)
DECLARE_POINTER(missile)
DECLARE_POINTER(mate)
DECLARE_POINTER(last_mate)
END_POINTERS
DBot::DBot ()
: DThinker(STAT_BOT)
{
Clear ();
}
void DBot::Clear ()
{
player = NULL;
angle = 0;
dest = NULL;
prev = NULL;
enemy = NULL;
missile = NULL;
mate = NULL;
last_mate = NULL;
memset(&skill, 0, sizeof(skill));
t_active = 0;
t_respawn = 0;
t_strafe = 0;
t_react = 0;
t_fight = 0;
t_roam = 0;
t_rocket = 0;
first_shot = true;
sleft = false;
allround = false;
increase = false;
oldx = 0;
oldy = 0;
}
void DBot::Serialize (FArchive &arc)
{
Super::Serialize (arc);
if (SaveVersion < 4515)
{
angle_t savedyaw;
int savedpitch;
arc << savedyaw
<< savedpitch;
}
else
{
arc << player;
}
arc << angle
<< dest
<< prev
<< enemy
<< missile
<< mate
<< last_mate
<< skill
<< t_active
<< t_respawn
<< t_strafe
<< t_react
<< t_fight
<< t_roam
<< t_rocket
<< first_shot
<< sleft
<< allround
<< increase
<< oldx
<< oldy;
}
void DBot::Tick ()
{
Super::Tick ();
if (player->mo == NULL || bglobal.freeze)
{
return;
}
BotThinkCycles.Clock();
bglobal.m_Thinking = true;
Think ();
bglobal.m_Thinking = false;
BotThinkCycles.Unclock();
}
CVAR (Int, bot_next_color, 11, 0)
CVAR (Bool, bot_observer, false, 0)
@ -55,9 +151,14 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam)
bot = bot->next;
if (bot)
{
bot->inuse = false;
bot->inuse = BOTINUSE_No;
bot->lastteam = keepTeam ? players[i].userinfo.GetTeam() : TEAM_NONE;
}
if (players[i].Bot != NULL)
{
players[i].Bot->Destroy ();
players[i].Bot = NULL;
}
players[i].~player_t();
::new(&players[i]) player_t;
players[i].userinfo.Reset();
@ -66,6 +167,12 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam)
CCMD (removebots)
{
if (!players[consoleplayer].settings_controller)
{
Printf ("Only setting controllers can remove bots\n");
return;
}
Net_WriteByte (DEM_KILLBOTS);
}
@ -91,7 +198,7 @@ CCMD (listbots)
while (thebot)
{
Printf ("%s%s\n", thebot->name, thebot->inuse ? " (active)" : "");
Printf ("%s%s\n", thebot->name, thebot->inuse == BOTINUSE_Yes ? " (active)" : "");
thebot = thebot->next;
count++;
}

View File

@ -14,6 +14,7 @@
#include "d_ticcmd.h"
#include "r_defs.h"
#include "a_pickups.h"
#include "stats.h"
#define FORWARDWALK 0x1900
#define FORWARDRUN 0x3200
@ -60,6 +61,13 @@ struct botskill_t
FArchive &operator<< (FArchive &arc, botskill_t &skill);
enum
{
BOTINUSE_No,
BOTINUSE_Waiting,
BOTINUSE_Yes,
};
//Info about all bots in the bots.cfg
//Updated during each level start.
//Info given to bots when they're spawned.
@ -69,7 +77,7 @@ struct botinfo_t
char *name;
char *info;
botskill_t skill;
bool inuse;
int inuse;
int lastteam;
};
@ -81,35 +89,29 @@ public:
void ClearPlayer (int playernum, bool keepTeam);
//(B_Game.c)
void Main (int buf);
//(b_game.cpp)
void Main ();
void Init ();
void End();
void CleanBotstuff (player_t *p);
bool SpawnBot (const char *name, int color = NOCOLOR);
void TryAddBot (BYTE **stream, int player);
void RemoveAllBots (bool fromlist);
bool LoadBots ();
void ForgetBots ();
void DoAddBot (int bnum, char *info);
void RemoveAllBots (bool fromlist);
//(B_Func.c)
bool Check_LOS (AActor *mobj1, AActor *mobj2, angle_t vangle);
//(b_func.cpp)
void StartTravel ();
void FinishTravel ();
bool IsLeader (player_t *player);
void SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum);
fixed_t FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd);
bool SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FCheckPosition &tm);
//(B_Think.c)
void WhatToGet (AActor *actor, AActor *item);
//(B_move.c)
void Roam (AActor *actor, ticcmd_t *cmd);
bool Move (AActor *actor, ticcmd_t *cmd);
bool TryWalk (AActor *actor, ticcmd_t *cmd);
void NewChaseDir (AActor *actor, ticcmd_t *cmd);
//(b_move.cpp)
bool CleanAhead (AActor *thing, fixed_t x, fixed_t y, ticcmd_t *cmd);
void TurnToAng (AActor *actor);
void Pitch (AActor *actor, AActor *target);
bool IsDangerous (sector_t *sec);
TArray<FString> getspawned; //Array of bots (their names) which should be spawned when starting a game.
bool botingame[MAXPLAYERS];
BYTE freeze:1; //Game in freeze mode.
BYTE changefreeze:1; //Game wants to change freeze mode.
int botnum;
@ -123,20 +125,8 @@ public:
bool m_Thinking;
private:
//(B_Func.c)
bool Reachable (AActor *actor, AActor *target);
void Dofire (AActor *actor, ticcmd_t *cmd);
AActor *Choose_Mate (AActor *bot);
AActor *Find_enemy (AActor *bot);
void SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum);
fixed_t FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd);
angle_t FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd);
bool SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FCheckPosition &tm);
//(B_Think.c)
void Think (AActor *actor, ticcmd_t *cmd);
void ThinkForMove (AActor *actor, ticcmd_t *cmd);
void Set_enemy (AActor *actor);
//(b_game.cpp)
bool DoAddBot (BYTE *info, botskill_t skill);
protected:
bool ctf;
@ -145,9 +135,84 @@ protected:
bool observer; //Consoleplayer is observer.
};
class DBot : public DThinker
{
DECLARE_CLASS(DBot,DThinker)
HAS_OBJECT_POINTERS
public:
DBot ();
void Clear ();
void Serialize (FArchive &arc);
void Tick ();
//(b_think.cpp)
void WhatToGet (AActor *item);
//(b_func.cpp)
bool Check_LOS (AActor *to, angle_t vangle);
player_t *player;
angle_t angle; // The wanted angle that the bot try to get every tic.
// (used to get a smooth view movement)
TObjPtr<AActor> dest; // Move Destination.
TObjPtr<AActor> prev; // Previous move destination.
TObjPtr<AActor> enemy; // The dead meat.
TObjPtr<AActor> missile; // A threatening missile that needs to be avoided.
TObjPtr<AActor> mate; // Friend (used for grouping in teamplay or coop).
TObjPtr<AActor> last_mate; // If bots mate disappeared (not if died) that mate is
// pointed to by this. Allows bot to roam to it if
// necessary.
//Skills
struct botskill_t skill;
//Tickers
int t_active; // Open door, lower lift stuff, door must open and
// lift must go down before bot does anything
// radical like try a stuckmove
int t_respawn;
int t_strafe;
int t_react;
int t_fight;
int t_roam;
int t_rocket;
//Misc booleans
bool first_shot; // Used for reaction skill.
bool sleft; // If false, strafe is right.
bool allround;
bool increase;
fixed_t oldx;
fixed_t oldy;
private:
//(B_think.cpp)
void Think ();
void ThinkForMove (ticcmd_t *cmd);
void Set_enemy ();
//(B_func.cpp)
bool Reachable (AActor *target);
void Dofire (ticcmd_t *cmd);
AActor *Choose_Mate ();
AActor *Find_enemy ();
angle_t FireRox (AActor *enemy, ticcmd_t *cmd);
//(b_move.cpp)
void Roam (ticcmd_t *cmd);
bool Move (ticcmd_t *cmd);
bool TryWalk (ticcmd_t *cmd);
void NewChaseDir (ticcmd_t *cmd);
void TurnToAng ();
void Pitch (AActor *target);
};
//Externs
extern FCajunMaster bglobal;
extern cycle_t BotThinkCycles, BotSupportCycles;
EXTERN_CVAR (Float, bot_flag_return_time)
EXTERN_CVAR (Int, bot_next_color)
@ -158,7 +223,3 @@ EXTERN_CVAR (Bool, bot_watersplash)
EXTERN_CVAR (Bool, bot_chat)
#endif // __B_BOT_H__

View File

@ -24,24 +24,23 @@
static FRandom pr_botdofire ("BotDoFire");
//Checks TRUE reachability from
//one looker to another. First mobj (looker) is looker.
bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget)
//Checks TRUE reachability from bot to a looker.
bool DBot::Reachable (AActor *rtarget)
{
if (looker == rtarget)
if (player->mo == rtarget)
return false;
if ((rtarget->Sector->ceilingplane.ZatPoint (rtarget->x, rtarget->y) -
rtarget->Sector->floorplane.ZatPoint (rtarget->x, rtarget->y))
< looker->height) //Where rtarget is, looker can't be.
< player->mo->height) //Where rtarget is, player->mo can't be.
return false;
sector_t *last_s = looker->Sector;
fixed_t last_z = last_s->floorplane.ZatPoint (looker->x, looker->y);
fixed_t estimated_dist = P_AproxDistance (looker->x - rtarget->x, looker->y - rtarget->y);
sector_t *last_s = player->mo->Sector;
fixed_t last_z = last_s->floorplane.ZatPoint (player->mo->x, player->mo->y);
fixed_t estimated_dist = P_AproxDistance (player->mo->x - rtarget->x, player->mo->y - rtarget->y);
bool reachable = true;
FPathTraverse it(looker->x+looker->velx, looker->y+looker->vely, rtarget->x, rtarget->y, PT_ADDLINES|PT_ADDTHINGS);
FPathTraverse it(player->mo->x+player->mo->velx, player->mo->y+player->mo->vely, rtarget->x, rtarget->y, PT_ADDLINES|PT_ADDTHINGS);
intercept_t *in;
while ((in = it.Next()))
{
@ -55,8 +54,8 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget)
frac = in->frac - FixedDiv (4*FRACUNIT, MAX_TRAVERSE_DIST);
dist = FixedMul (frac, MAX_TRAVERSE_DIST);
hitx = it.Trace().x + FixedMul (looker->velx, frac);
hity = it.Trace().y + FixedMul (looker->vely, frac);
hitx = it.Trace().x + FixedMul (player->mo->velx, frac);
hity = it.Trace().y + FixedMul (player->mo->vely, frac);
if (in->isaline)
{
@ -76,7 +75,7 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget)
if (!bglobal.IsDangerous (s) && //Any nukage/lava?
(floorheight <= (last_z+MAXMOVEHEIGHT)
&& ((ceilingheight == floorheight && line->special)
|| (ceilingheight - floorheight) >= looker->height))) //Does it fit?
|| (ceilingheight - floorheight) >= player->mo->height))) //Does it fit?
{
last_z = floorheight;
last_s = s;
@ -95,7 +94,7 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget)
}
thing = in->d.thing;
if (thing == looker) //Can't reach self in this case.
if (thing == player->mo) //Can't reach self in this case.
continue;
if (thing == rtarget && (rtarget->Sector->floorplane.ZatPoint (rtarget->x, rtarget->y) <= (last_z+MAXMOVEHEIGHT)))
{
@ -115,16 +114,16 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget)
//if these conditions are true, the function returns true.
//GOOD TO KNOW is that the player's view angle
//in doom is 90 degrees infront.
bool FCajunMaster::Check_LOS (AActor *from, AActor *to, angle_t vangle)
bool DBot::Check_LOS (AActor *to, angle_t vangle)
{
if (!P_CheckSight (from, to, SF_SEEPASTBLOCKEVERYTHING))
if (!P_CheckSight (player->mo, to, SF_SEEPASTBLOCKEVERYTHING))
return false; // out of sight
if (vangle == ANGLE_MAX)
return true;
if (vangle == 0)
return false; //Looker seems to be blind.
return (angle_t)abs (R_PointToAngle2 (from->x, from->y, to->x, to->y) - from->angle) <= vangle/2;
return (angle_t)abs (R_PointToAngle2 (player->mo->x, player->mo->y, to->x, to->y) - player->mo->angle) <= vangle/2;
}
//-------------------------------------
@ -132,7 +131,7 @@ bool FCajunMaster::Check_LOS (AActor *from, AActor *to, angle_t vangle)
//-------------------------------------
//The bot will check if it's time to fire
//and do so if that is the case.
void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd)
void DBot::Dofire (ticcmd_t *cmd)
{
bool no_fire; //used to prevent bot from pumping rockets into nearby walls.
int aiming_penalty=0; //For shooting at shading target, if screen is red, MAKEME: When screen red.
@ -140,50 +139,48 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd)
fixed_t dist;
angle_t an;
int m;
static bool inc[MAXPLAYERS];
AActor *enemy = actor->player->enemy;
if (!enemy || !(enemy->flags & MF_SHOOTABLE) || enemy->health <= 0)
return;
if (actor->player->ReadyWeapon == NULL)
if (player->ReadyWeapon == NULL)
return;
if (actor->player->damagecount > actor->player->skill.isp)
if (player->damagecount > skill.isp)
{
actor->player->first_shot = true;
first_shot = true;
return;
}
//Reaction skill thing.
if (actor->player->first_shot &&
!(actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING))
if (first_shot &&
!(player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING))
{
actor->player->t_react = (100-actor->player->skill.reaction+1)/((pr_botdofire()%3)+3);
t_react = (100-skill.reaction+1)/((pr_botdofire()%3)+3);
}
actor->player->first_shot = false;
if (actor->player->t_react)
first_shot = false;
if (t_react)
return;
//MAKEME: Decrease the rocket suicides even more.
no_fire = true;
//actor->player->angle = R_PointToAngle2(actor->x, actor->y, actor->player->enemy->x, actor->player->enemy->y);
//angle = R_PointToAngle2(player->mo->x, player->mo->y, player->enemy->x, player->enemy->y);
//Distance to enemy.
dist = P_AproxDistance ((actor->x + actor->velx) - (enemy->x + enemy->velx),
(actor->y + actor->vely) - (enemy->y + enemy->vely));
dist = P_AproxDistance ((player->mo->x + player->mo->velx) - (enemy->x + enemy->velx),
(player->mo->y + player->mo->vely) - (enemy->y + enemy->vely));
//FIRE EACH TYPE OF WEAPON DIFFERENT: Here should all the different weapons go.
if (actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)
if (player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)
{
if ((actor->player->ReadyWeapon->ProjectileType != NULL))
if ((player->ReadyWeapon->ProjectileType != NULL))
{
if (actor->player->ReadyWeapon->CheckAmmo (AWeapon::PrimaryFire, false, true))
if (player->ReadyWeapon->CheckAmmo (AWeapon::PrimaryFire, false, true))
{
// This weapon can fire a projectile and has enough ammo to do so
goto shootmissile;
}
else if (!(actor->player->ReadyWeapon->WeaponFlags & WIF_AMMO_OPTIONAL))
else if (!(player->ReadyWeapon->WeaponFlags & WIF_AMMO_OPTIONAL))
{
// Ammo is required, so don't shoot. This is for weapons that shoot
// missiles that die at close range, such as the powered-up Phoneix Rod.
@ -196,51 +193,51 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd)
no_fire = (dist > (MELEERANGE*4));
}
}
else if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG)
else if (player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG)
{
//MAKEME: This should be smarter.
if ((pr_botdofire()%200)<=actor->player->skill.reaction)
if(Check_LOS(actor, actor->player->enemy, SHOOTFOV))
if ((pr_botdofire()%200)<=skill.reaction)
if(Check_LOS(enemy, SHOOTFOV))
no_fire = false;
}
else if (actor->player->ReadyWeapon->ProjectileType != NULL)
else if (player->ReadyWeapon->ProjectileType != NULL)
{
if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE)
if (player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE)
{
//Special rules for RL
an = FireRox (actor, enemy, cmd);
an = FireRox (enemy, cmd);
if(an)
{
actor->player->angle = an;
angle = an;
//have to be somewhat precise. to avoid suicide.
if (abs (actor->player->angle - actor->angle) < 12*ANGLE_1)
if (abs (angle - player->mo->angle) < 12*ANGLE_1)
{
actor->player->t_rocket = 9;
t_rocket = 9;
no_fire = false;
}
}
}
// prediction aiming
shootmissile:
dist = P_AproxDistance (actor->x - enemy->x, actor->y - enemy->y);
m = dist / GetDefaultByType (actor->player->ReadyWeapon->ProjectileType)->Speed;
SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1);
actor->player->angle = R_PointToAngle2 (actor->x, actor->y, body1->x, body1->y);
if (Check_LOS (actor, enemy, SHOOTFOV))
dist = P_AproxDistance (player->mo->x - enemy->x, player->mo->y - enemy->y);
m = dist / GetDefaultByType (player->ReadyWeapon->ProjectileType)->Speed;
bglobal.SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1);
angle = R_PointToAngle2 (player->mo->x, player->mo->y, bglobal.body1->x, bglobal.body1->y);
if (Check_LOS (enemy, SHOOTFOV))
no_fire = false;
}
else
{
//Other weapons, mostly instant hit stuff.
actor->player->angle = R_PointToAngle2 (actor->x, actor->y, enemy->x, enemy->y);
angle = R_PointToAngle2 (player->mo->x, player->mo->y, enemy->x, enemy->y);
aiming_penalty = 0;
if (enemy->flags & MF_SHADOW)
aiming_penalty += (pr_botdofire()%25)+10;
if (enemy->Sector->lightlevel<WHATS_DARK/* && !(actor->player->powers & PW_INFRARED)*/)
if (enemy->Sector->lightlevel<WHATS_DARK/* && !(player->powers & PW_INFRARED)*/)
aiming_penalty += pr_botdofire()%40;//Dark
if (actor->player->damagecount)
aiming_penalty += actor->player->damagecount; //Blood in face makes it hard to aim
aiming_value = actor->player->skill.aiming - aiming_penalty;
if (player->damagecount)
aiming_penalty += player->damagecount; //Blood in face makes it hard to aim
aiming_value = skill.aiming - aiming_penalty;
if (aiming_value <= 0)
aiming_value = 1;
m = ((SHOOTFOV/2)-(aiming_value*SHOOTFOV/200)); //Higher skill is more accurate
@ -249,18 +246,18 @@ shootmissile:
if (m)
{
if (inc[actor->id])
actor->player->angle += m;
if (increase)
angle += m;
else
actor->player->angle -= m;
angle -= m;
}
if (abs (actor->player->angle - actor->angle) < 4*ANGLE_1)
if (abs (angle - player->mo->angle) < 4*ANGLE_1)
{
inc[actor->id] = !inc[actor->id];
increase = !increase;
}
if (Check_LOS (actor, enemy, (SHOOTFOV/2)))
if (Check_LOS (enemy, (SHOOTFOV/2)))
no_fire = false;
}
if (!no_fire) //If going to fire weapon
@ -268,53 +265,48 @@ shootmissile:
cmd->ucmd.buttons |= BT_ATTACK;
}
//Prevents bot from jerking, when firing automatic things with low skill.
//actor->angle = R_PointToAngle2(actor->x, actor->y, actor->player->enemy->x, actor->player->enemy->y);
//player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->enemy->x, player->enemy->y);
}
bool FCajunMaster::IsLeader (player_t *player)
{
for (int count = 0; count < MAXPLAYERS; count++)
{
if (players[count].Bot != NULL
&& players[count].Bot->mate == player->mo)
{
return true;
}
}
return false;
}
//This function is called every
//tick (for each bot) to set
//the mate (teammate coop mate).
AActor *FCajunMaster::Choose_Mate (AActor *bot)
AActor *DBot::Choose_Mate ()
{
int count;
int count2;
fixed_t closest_dist, test;
AActor *target;
AActor *observer;
bool p_leader[MAXPLAYERS];
//is mate alive?
if (bot->player->mate)
if (mate)
{
if (bot->player->mate->health <= 0)
bot->player->mate = NULL;
if (mate->health <= 0)
mate = NULL;
else
bot->player->last_mate = bot->player->mate;
last_mate = mate;
}
if (bot->player->mate) //Still is..
return bot->player->mate;
if (mate) //Still is..
return mate;
//Check old_mates status.
if (bot->player->last_mate)
if (bot->player->last_mate->health <= 0)
bot->player->last_mate = NULL;
for (count = 0; count < MAXPLAYERS; count++)
{
if (!playeringame[count])
continue;
p_leader[count] = false;
for (count2 = 0; count2 < MAXPLAYERS; count2++)
{
if (players[count].isbot
&& players[count2].mate == players[count].mo)
{
p_leader[count] = true;
break;
}
}
}
if (last_mate)
if (last_mate->health <= 0)
last_mate = NULL;
target = NULL;
closest_dist = FIXED_MAX;
@ -330,18 +322,17 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot)
if (playeringame[count]
&& client->mo
&& bot != client->mo
&& (bot->IsTeammate (client->mo) || !deathmatch)
&& player->mo != client->mo
&& (player->mo->IsTeammate (client->mo) || !deathmatch)
&& client->mo->health > 0
&& client->mo != observer
&& ((bot->health/2) <= client->mo->health || !deathmatch)
&& !p_leader[count]) //taken?
&& ((player->mo->health/2) <= client->mo->health || !deathmatch)
&& !bglobal.IsLeader(client)) //taken?
{
if (P_CheckSight (bot, client->mo, SF_IGNOREVISIBILITY))
if (P_CheckSight (player->mo, client->mo, SF_IGNOREVISIBILITY))
{
test = P_AproxDistance (client->mo->x - bot->x,
client->mo->y - bot->y);
test = P_AproxDistance (client->mo->x - player->mo->x,
client->mo->y - player->mo->y);
if (test < closest_dist)
{
@ -354,15 +345,15 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot)
/*
//Make a introducing to mate.
if(target && target!=bot->player->last_mate)
if(target && target!=last_mate)
{
if((P_Random()%(200*bglobal.botnum))<3)
{
bot->player->chat = c_teamup;
chat = c_teamup;
if(target->bot)
strcpy(bot->player->c_target, botsingame[target->bot_id]);
strcpy(c_target, botsingame[target->bot_id]);
else if(target->player)
strcpy(bot->player->c_target, player_names[target->play_id]);
strcpy(c_target, player_names[target->play_id]);
}
}
*/
@ -372,7 +363,7 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot)
}
//MAKEME: Make this a smart decision
AActor *FCajunMaster::Find_enemy (AActor *bot)
AActor *DBot::Find_enemy ()
{
int count;
fixed_t closest_dist, temp; //To target.
@ -382,15 +373,15 @@ AActor *FCajunMaster::Find_enemy (AActor *bot)
if (!deathmatch)
{ // [RH] Take advantage of the Heretic/Hexen code to be a little smarter
return P_RoughMonsterSearch (bot, 20);
return P_RoughMonsterSearch (player->mo, 20);
}
//Note: It's hard to ambush a bot who is not alone
if (bot->player->allround || bot->player->mate)
if (allround || mate)
vangle = ANGLE_MAX;
else
vangle = ENEMY_SCAN_FOV;
bot->player->allround = false;
allround = false;
target = NULL;
closest_dist = FIXED_MAX;
@ -403,21 +394,21 @@ AActor *FCajunMaster::Find_enemy (AActor *bot)
{
player_t *client = &players[count];
if (playeringame[count]
&& !bot->IsTeammate (client->mo)
&& !player->mo->IsTeammate (client->mo)
&& client->mo != observer
&& client->mo->health > 0
&& bot != client->mo)
&& player->mo != client->mo)
{
if (Check_LOS (bot, client->mo, vangle)) //Here's a strange one, when bot is standing still, the P_CheckSight within Check_LOS almost always returns false. tought it should be the same checksight as below but.. (below works) something must be fuckin wierd screded up.
//if(P_CheckSight( bot, players[count].mo))
if (Check_LOS (client->mo, vangle)) //Here's a strange one, when bot is standing still, the P_CheckSight within Check_LOS almost always returns false. tought it should be the same checksight as below but.. (below works) something must be fuckin wierd screded up.
//if(P_CheckSight(player->mo, players[count].mo))
{
temp = P_AproxDistance (client->mo->x - bot->x,
client->mo->y - bot->y);
temp = P_AproxDistance (client->mo->x - player->mo->x,
client->mo->y - player->mo->y);
//Too dark?
if (temp > DARK_DIST &&
client->mo->Sector->lightlevel < WHATS_DARK /*&&
bot->player->Powers & PW_INFRARED*/)
player->Powers & PW_INFRARED*/)
continue;
if (temp < closest_dist)
@ -501,16 +492,16 @@ fixed_t FCajunMaster::FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd)
return dist;
}
angle_t FCajunMaster::FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd)
angle_t DBot::FireRox (AActor *enemy, ticcmd_t *cmd)
{
fixed_t dist;
angle_t ang;
AActor *actor;
int m;
SetBodyAt (bot->x + FixedMul(bot->velx, 5*FRACUNIT),
bot->y + FixedMul(bot->vely, 5*FRACUNIT),
bot->z + (bot->height / 2), 2);
bglobal.SetBodyAt (player->mo->x + FixedMul(player->mo->velx, 5*FRACUNIT),
player->mo->y + FixedMul(player->mo->vely, 5*FRACUNIT),
player->mo->z + (player->mo->height / 2), 2);
actor = bglobal.body2;
@ -520,16 +511,16 @@ angle_t FCajunMaster::FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd)
//Predict.
m = (((dist+1)/FRACUNIT) / GetDefaultByName("Rocket")->Speed);
SetBodyAt (enemy->x + FixedMul(enemy->velx, (m+2*FRACUNIT)),
enemy->y + FixedMul(enemy->vely, (m+2*FRACUNIT)), ONFLOORZ, 1);
bglobal.SetBodyAt (enemy->x + FixedMul(enemy->velx, (m+2*FRACUNIT)),
enemy->y + FixedMul(enemy->vely, (m+2*FRACUNIT)), ONFLOORZ, 1);
dist = P_AproxDistance(actor->x-bglobal.body1->x, actor->y-bglobal.body1->y);
//try the predicted location
if (P_CheckSight (actor, bglobal.body1, SF_IGNOREVISIBILITY)) //See the predicted location, so give a test missile
{
FCheckPosition tm;
if (SafeCheckPosition (bot, actor->x, actor->y, tm))
if (bglobal.SafeCheckPosition (player->mo, actor->x, actor->y, tm))
{
if (FakeFire (actor, bglobal.body1, cmd) >= SAFE_SELF_MISDIST)
if (bglobal.FakeFire (actor, bglobal.body1, cmd) >= SAFE_SELF_MISDIST)
{
ang = R_PointToAngle2 (actor->x, actor->y, bglobal.body1->x, bglobal.body1->y);
return ang;
@ -539,9 +530,9 @@ angle_t FCajunMaster::FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd)
//Try fire straight.
if (P_CheckSight (actor, enemy, 0))
{
if (FakeFire (bot, enemy, cmd) >= SAFE_SELF_MISDIST)
if (bglobal.FakeFire (player->mo, enemy, cmd) >= SAFE_SELF_MISDIST)
{
ang = R_PointToAngle2(bot->x, bot->y, enemy->x, enemy->y);
ang = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y);
return ang;
}
}
@ -559,3 +550,25 @@ bool FCajunMaster::SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FChec
actor->flags = savedFlags;
return res;
}
void FCajunMaster::StartTravel ()
{
for (int i = 0; i < MAXPLAYERS; ++i)
{
if (players[i].Bot != NULL)
{
players[i].Bot->ChangeStatNum (STAT_TRAVELLING);
}
}
}
void FCajunMaster::FinishTravel ()
{
for (int i = 0; i < MAXPLAYERS; ++i)
{
if (players[i].Bot != NULL)
{
players[i].Bot->ChangeStatNum (STAT_BOT);
}
}
}

View File

@ -89,49 +89,27 @@ enum
BOTCFG_TEAM
};
static bool waitingforspawn[MAXPLAYERS];
FCajunMaster::~FCajunMaster()
{
ForgetBots();
}
//This function is called every tick (from g_game.c),
//send bots into thinking (+more).
void FCajunMaster::Main (int buf)
//This function is called every tick (from g_game.c).
void FCajunMaster::Main ()
{
int i;
BotThinkCycles.Reset();
if (consoleplayer != Net_Arbitrator || demoplayback)
if (demoplayback || gamestate != GS_LEVEL || consoleplayer != Net_Arbitrator)
return;
if (gamestate != GS_LEVEL)
return;
m_Thinking = true;
//Think for bots.
if (botnum)
{
BotThinkCycles.Clock();
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].mo && !freeze && players[i].isbot)
Think (players[i].mo, &netcmds[i][buf]);
}
BotThinkCycles.Unclock();
}
//Add new bots?
if (wanted_botnum > botnum && !freeze)
{
if (t_join == ((wanted_botnum - botnum) * SPAWN_DELAY))
{
if (!SpawnBot (getspawned[spawn_tries]))
if (!SpawnBot (getspawned[spawn_tries]))
wanted_botnum--;
spawn_tries++;
spawn_tries++;
}
t_join--;
@ -156,14 +134,10 @@ void FCajunMaster::Main (int buf)
players[consoleplayer].mo->flags2 &= ~MF2_FLY;
players[consoleplayer].mo->LinkToWorld ();
}
m_Thinking = false;
}
void FCajunMaster::Init ()
{
int i;
botnum = 0;
firstthing = NULL;
spawn_tries = 0;
@ -172,18 +146,6 @@ void FCajunMaster::Init ()
body1 = NULL;
body2 = NULL;
//Remove all bots upon each level start, they'll get spawned instead.
for (i = 0; i < MAXPLAYERS; i++)
{
waitingforspawn[i] = false;
if (playeringame[i] && players[i].isbot)
{
CleanBotstuff (&players[i]);
players[i].isbot = false;
botingame[i] = false;
}
}
if (ctf && teamplay == false)
teamplay = true; //Need teamplay for ctf. (which is not done yet)
@ -199,7 +161,7 @@ void FCajunMaster::Init ()
while (thebot != NULL)
{
thebot->inuse = false;
thebot->inuse = BOTINUSE_No;
thebot = thebot->next;
}
}
@ -212,19 +174,13 @@ void FCajunMaster::End ()
//Arrange wanted botnum and their names, so they can be spawned next level.
getspawned.Clear();
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].isbot)
{
if (deathmatch)
{
getspawned.Push(players[i].userinfo.GetName());
}
CleanBotstuff (&players[i]);
}
}
if (deathmatch)
{
for (i = 0; i < MAXPLAYERS; i++)
{
getspawned.Push(players[i].userinfo.GetName());
}
wanted_botnum = botnum;
}
}
@ -240,12 +196,10 @@ void FCajunMaster::End ()
//The color parameter can be either a
//color (range from 0-10), or = NOCOLOR.
//The color parameter overides bots
//induvidual colors if not = NOCOLOR.
//individual colors if not = NOCOLOR.
bool FCajunMaster::SpawnBot (const char *name, int color)
{
int playernumber;
//COLORS
static const char colors[11][17] =
{
@ -262,36 +216,31 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
"\\color\\cf df 90" //10 = Bleached Bone
};
for (playernumber = 0; playernumber < MAXPLAYERS; playernumber++)
{
if (!playeringame[playernumber] && !waitingforspawn[playernumber])
{
break;
}
}
if (playernumber == MAXPLAYERS)
{
Printf ("The maximum of %d players/bots has been reached\n", MAXPLAYERS);
return false;
}
botinfo_t *thebot;
int botshift;
if (name)
{
thebot = botinfo;
// Check if exist or already in the game.
botshift = 0;
while (thebot && stricmp (name, thebot->name))
{
thebot = thebot->next;
botshift++;
}
if (thebot == NULL)
{
Printf ("couldn't find %s in %s\n", name, BOTFILENAME);
return false;
}
else if (thebot->inuse)
else if (thebot->inuse == BOTINUSE_Waiting)
{
return false;
}
else if (thebot->inuse == BOTINUSE_Yes)
{
Printf ("%s is already in the thick\n", name);
return false;
@ -304,9 +253,13 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
{
int rnum = (pr_botspawn() % loaded_bots);
thebot = botinfo;
botshift = 0;
while (rnum)
{
--rnum, thebot = thebot->next;
if (!thebot->inuse)
botshift++;
}
if (thebot->inuse == BOTINUSE_No)
vacant = true;
}
}
@ -316,10 +269,10 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
return false;
}
waitingforspawn[playernumber] = true;
thebot->inuse = BOTINUSE_Waiting;
Net_WriteByte (DEM_ADDBOT);
Net_WriteByte (playernumber);
Net_WriteByte (botshift);
{
//Set color.
char concat[512];
@ -335,52 +288,106 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
}
Net_WriteString (concat);
}
players[playernumber].skill = thebot->skill;
thebot->inuse = true;
//Increment this.
botnum++;
Net_WriteByte(thebot->skill.aiming);
Net_WriteByte(thebot->skill.perfection);
Net_WriteByte(thebot->skill.reaction);
Net_WriteByte(thebot->skill.isp);
return true;
}
void FCajunMaster::DoAddBot (int bnum, char *info)
void FCajunMaster::TryAddBot (BYTE **stream, int player)
{
BYTE *infob = (BYTE *)info;
D_ReadUserInfoStrings (bnum, &infob, false);
int botshift = ReadByte (stream);
char *info = ReadString (stream);
botskill_t skill;
skill.aiming = ReadByte (stream);
skill.perfection = ReadByte (stream);
skill.reaction = ReadByte (stream);
skill.isp = ReadByte (stream);
botinfo_t *thebot = NULL;
if (consoleplayer == player)
{
thebot = botinfo;
while (botshift > 0)
{
thebot = thebot->next;
botshift--;
}
}
if (DoAddBot ((BYTE *)info, skill))
{
//Increment this.
botnum++;
if (thebot != NULL)
{
thebot->inuse = BOTINUSE_Yes;
}
}
else
{
if (thebot != NULL)
{
thebot->inuse = BOTINUSE_No;
}
}
delete[] info;
}
bool FCajunMaster::DoAddBot (BYTE *info, botskill_t skill)
{
int bnum;
for (bnum = 0; bnum < MAXPLAYERS; bnum++)
{
if (!playeringame[bnum])
{
break;
}
}
if (bnum == MAXPLAYERS)
{
Printf ("The maximum of %d players/bots has been reached\n", MAXPLAYERS);
return false;
}
D_ReadUserInfoStrings (bnum, &info, false);
if (!deathmatch && playerstarts[bnum].type == 0)
{
Printf ("%s tried to join, but there was no player %d start\n",
players[bnum].userinfo.GetName(), bnum+1);
ClearPlayer (bnum, false); // Make the bot inactive again
if (botnum > 0)
{
botnum--;
}
return false;
}
multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost).
players[bnum].Bot = new DBot;
players[bnum].Bot->player = &players[bnum];
players[bnum].Bot->skill = skill;
playeringame[bnum] = true;
players[bnum].mo = NULL;
players[bnum].playerstate = PST_ENTER;
if (teamplay)
Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName());
else
Printf ("%s joined the game\n", players[bnum].userinfo.GetName());
G_DoReborn (bnum, true);
if (StatusBar != NULL)
{
multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost).
players[bnum].isbot = true;
playeringame[bnum] = true;
players[bnum].mo = NULL;
players[bnum].playerstate = PST_ENTER;
botingame[bnum] = true;
if (teamplay)
Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName());
else
Printf ("%s joined the game\n", players[bnum].userinfo.GetName());
G_DoReborn (bnum, true);
if (StatusBar != NULL)
{
StatusBar->MultiplayerChanged ();
}
StatusBar->MultiplayerChanged ();
}
waitingforspawn[bnum] = false;
return true;
}
void FCajunMaster::RemoveAllBots (bool fromlist)
@ -389,13 +396,13 @@ void FCajunMaster::RemoveAllBots (bool fromlist)
for (i = 0; i < MAXPLAYERS; ++i)
{
if (playeringame[i] && botingame[i])
if (players[i].Bot != NULL)
{
// If a player is looking through this bot's eyes, make him
// look through his own eyes instead.
for (j = 0; j < MAXPLAYERS; ++j)
{
if (i != j && playeringame[j] && !botingame[j])
if (i != j && playeringame[j] && players[j].Bot == NULL)
{
if (players[j].camera == players[i].mo)
{
@ -415,34 +422,10 @@ void FCajunMaster::RemoveAllBots (bool fromlist)
if (fromlist)
{
wanted_botnum = 0;
for (i = 0; i < MAXPLAYERS; i++)
waitingforspawn[i] = false;
}
botnum = 0;
}
//Clean the bot part of the player_t
//Used when bots are respawned or at level starts.
void FCajunMaster::CleanBotstuff (player_t *p)
{
p->angle = ANG45;
p->dest = NULL;
p->enemy = NULL; //The dead meat.
p->missile = NULL; //A threatening missile that needs to be avoided.
p->mate = NULL; //Friend (used for grouping in templay or coop.
p->last_mate = NULL; //If bot's mate dissapeared (not if died) that mate is pointed to by this. Allows bot to roam to it if necessary.
//Tickers
p->t_active = 0; //Open door, lower lift stuff, door must open and lift must go down before bot does anything radical like try a stuckmove
p->t_respawn = 0;
p->t_strafe = 0;
p->t_react = 0;
//Misc bools
p->isbot = true; //Important.
p->first_shot = true; //Used for reaction skill.
p->sleft = false; //If false, strafe is right.
p->allround = false;
}
//------------------
//Reads data for bot from

View File

@ -17,20 +17,7 @@
#include "gi.h"
#include "a_keys.h"
#include "d_event.h"
enum dirtype_t
{
DI_EAST,
DI_NORTHEAST,
DI_NORTH,
DI_NORTHWEST,
DI_WEST,
DI_SOUTHWEST,
DI_SOUTH,
DI_SOUTHEAST,
DI_NODIR,
NUMDIRS
};
#include "p_enemy.h"
static FRandom pr_botopendoor ("BotOpenDoor");
static FRandom pr_bottrywalk ("BotTryWalk");
@ -39,62 +26,58 @@ static FRandom pr_botnewchasedir ("BotNewChaseDir");
// borrow some tables from p_enemy.cpp
extern dirtype_t opposite[9];
extern dirtype_t diags[4];
extern fixed_t xspeed[8];
extern fixed_t yspeed[8];
extern TArray<line_t *> spechit;
//Called while the bot moves after its player->dest mobj
//Called while the bot moves after its dest mobj
//which can be a weapon/enemy/item whatever.
void FCajunMaster::Roam (AActor *actor, ticcmd_t *cmd)
void DBot::Roam (ticcmd_t *cmd)
{
int delta;
if (Reachable(actor, actor->player->dest))
if (Reachable(dest))
{ // Straight towards it.
actor->player->angle = R_PointToAngle2(actor->x, actor->y, actor->player->dest->x, actor->player->dest->y);
angle = R_PointToAngle2(player->mo->x, player->mo->y, dest->x, dest->y);
}
else if (actor->movedir < 8) // turn towards movement direction if not there yet
else if (player->mo->movedir < 8) // turn towards movement direction if not there yet
{
actor->player->angle &= (angle_t)(7<<29);
delta = actor->player->angle - (actor->movedir << 29);
angle &= (angle_t)(7<<29);
delta = angle - (player->mo->movedir << 29);
if (delta > 0)
actor->player->angle -= ANG45;
angle -= ANG45;
else if (delta < 0)
actor->player->angle += ANG45;
angle += ANG45;
}
// chase towards destination.
if (--actor->movecount < 0 || !Move (actor, cmd))
if (--player->mo->movecount < 0 || !Move (cmd))
{
NewChaseDir (actor, cmd);
NewChaseDir (cmd);
}
}
bool FCajunMaster::Move (AActor *actor, ticcmd_t *cmd)
bool DBot::Move (ticcmd_t *cmd)
{
fixed_t tryx, tryy;
bool try_ok;
int good;
if (actor->movedir == DI_NODIR)
if (player->mo->movedir == DI_NODIR)
return false;
if ((unsigned)actor->movedir >= 8)
if ((unsigned)player->mo->movedir >= 8)
I_Error ("Weird bot movedir!");
tryx = actor->x + 8*xspeed[actor->movedir];
tryy = actor->y + 8*yspeed[actor->movedir];
tryx = player->mo->x + 8*xspeed[player->mo->movedir];
tryy = player->mo->y + 8*yspeed[player->mo->movedir];
try_ok = CleanAhead (actor, tryx, tryy, cmd);
try_ok = bglobal.CleanAhead (player->mo, tryx, tryy, cmd);
if (!try_ok) //Anything blocking that could be opened etc..
{
if (!spechit.Size ())
return false;
actor->movedir = DI_NODIR;
player->mo->movedir = DI_NODIR;
good = 0;
line_t *ld;
@ -103,16 +86,16 @@ bool FCajunMaster::Move (AActor *actor, ticcmd_t *cmd)
{
bool tryit = true;
if (ld->special == Door_LockedRaise && !P_CheckKeys (actor, ld->args[3], false))
if (ld->special == Door_LockedRaise && !P_CheckKeys (player->mo, ld->args[3], false))
tryit = false;
else if (ld->special == Generic_Door && !P_CheckKeys (actor, ld->args[4], false))
else if (ld->special == Generic_Door && !P_CheckKeys (player->mo, ld->args[4], false))
tryit = false;
if (tryit &&
(P_TestActivateLine (ld, actor, 0, SPAC_Use) ||
P_TestActivateLine (ld, actor, 0, SPAC_Push)))
(P_TestActivateLine (ld, player->mo, 0, SPAC_Use) ||
P_TestActivateLine (ld, player->mo, 0, SPAC_Push)))
{
good |= ld == actor->BlockingLine ? 1 : 2;
good |= ld == player->mo->BlockingLine ? 1 : 2;
}
}
if (good && ((pr_botopendoor() >= 203) ^ (good & 1)))
@ -130,16 +113,16 @@ bool FCajunMaster::Move (AActor *actor, ticcmd_t *cmd)
return true;
}
bool FCajunMaster::TryWalk (AActor *actor, ticcmd_t *cmd)
bool DBot::TryWalk (ticcmd_t *cmd)
{
if (!Move (actor, cmd))
if (!Move (cmd))
return false;
actor->movecount = pr_bottrywalk() & 60;
player->mo->movecount = pr_bottrywalk() & 60;
return true;
}
void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
void DBot::NewChaseDir (ticcmd_t *cmd)
{
fixed_t deltax;
fixed_t deltay;
@ -151,7 +134,7 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
dirtype_t turnaround;
if (!actor->player->dest)
if (!dest)
{
#ifndef BOT_RELEASE_COMPILE
Printf ("Bot tried move without destination\n");
@ -159,11 +142,11 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
return;
}
olddir = (dirtype_t)actor->movedir;
olddir = (dirtype_t)player->mo->movedir;
turnaround = opposite[olddir];
deltax = actor->player->dest->x - actor->x;
deltay = actor->player->dest->y - actor->y;
deltax = dest->x - player->mo->x;
deltay = dest->y - player->mo->y;
if (deltax > 10*FRACUNIT)
d[1] = DI_EAST;
@ -182,8 +165,8 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
// try direct route
if (d[1] != DI_NODIR && d[2] != DI_NODIR)
{
actor->movedir = diags[((deltay<0)<<1)+(deltax>0)];
if (actor->movedir != turnaround && TryWalk(actor, cmd))
player->mo->movedir = diags[((deltay<0)<<1)+(deltax>0)];
if (player->mo->movedir != turnaround && TryWalk(cmd))
return;
}
@ -203,16 +186,16 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
if (d[1]!=DI_NODIR)
{
actor->movedir = d[1];
if (TryWalk (actor, cmd))
player->mo->movedir = d[1];
if (TryWalk (cmd))
return;
}
if (d[2]!=DI_NODIR)
{
actor->movedir = d[2];
player->mo->movedir = d[2];
if (TryWalk(actor, cmd))
if (TryWalk(cmd))
return;
}
@ -220,9 +203,9 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
// so pick another direction.
if (olddir!=DI_NODIR)
{
actor->movedir = olddir;
player->mo->movedir = olddir;
if (TryWalk(actor, cmd))
if (TryWalk(cmd))
return;
}
@ -235,9 +218,9 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
{
if (tdir!=turnaround)
{
actor->movedir = tdir;
player->mo->movedir = tdir;
if (TryWalk(actor, cmd))
if (TryWalk(cmd))
return;
}
}
@ -250,9 +233,9 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
{
if (tdir!=turnaround)
{
actor->movedir = tdir;
player->mo->movedir = tdir;
if (TryWalk(actor, cmd))
if (TryWalk(cmd))
return;
}
}
@ -260,12 +243,12 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd)
if (turnaround != DI_NODIR)
{
actor->movedir = turnaround;
if (TryWalk(actor, cmd))
player->mo->movedir = turnaround;
if (TryWalk(cmd))
return;
}
actor->movedir = DI_NODIR; // can not move
player->mo->movedir = DI_NODIR; // can not move
}
@ -324,48 +307,48 @@ bool FCajunMaster::CleanAhead (AActor *thing, fixed_t x, fixed_t y, ticcmd_t *cm
#define MAXTURN (15*ANGLE_1) //Max degrees turned in one tic. Lower is smother but may cause the bot not getting where it should = crash
#define TURNSENS 3 //Higher is smoother but slower turn.
void FCajunMaster::TurnToAng (AActor *actor)
void DBot::TurnToAng ()
{
int maxturn = MAXTURN;
if (actor->player->ReadyWeapon != NULL)
if (player->ReadyWeapon != NULL)
{
if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE)
if (player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE)
{
if (actor->player->t_roam && !actor->player->missile)
if (t_roam && !missile)
{ //Keep angle that where when shot where decided.
return;
}
}
if(actor->player->enemy)
if(!actor->player->dest) //happens when running after item in combat situations, or normal, prevents weak turns
if(actor->player->ReadyWeapon->ProjectileType == NULL && !(actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON))
if(Check_LOS(actor, actor->player->enemy, SHOOTFOV+5*ANGLE_1))
if(enemy)
if(!dest) //happens when running after item in combat situations, or normal, prevents weak turns
if(player->ReadyWeapon->ProjectileType == NULL && !(player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON))
if(Check_LOS(enemy, SHOOTFOV+5*ANGLE_1))
maxturn = 3;
}
int distance = actor->player->angle - actor->angle;
int distance = angle - player->mo->angle;
if (abs (distance) < OKAYRANGE && !actor->player->enemy)
if (abs (distance) < OKAYRANGE && !enemy)
return;
distance /= TURNSENS;
if (abs (distance) > maxturn)
distance = distance < 0 ? -maxturn : maxturn;
actor->angle += distance;
player->mo->angle += distance;
}
void FCajunMaster::Pitch (AActor *actor, AActor *target)
void DBot::Pitch (AActor *target)
{
double aim;
double diff;
diff = target->z - actor->z;
aim = atan (diff / (double)P_AproxDistance (actor->x - target->x, actor->y - target->y));
actor->pitch = -(int)(aim * ANGLE_180/M_PI);
diff = target->z - player->mo->z;
aim = atan (diff / (double)P_AproxDistance (player->mo->x - target->x, player->mo->y - target->y));
player->mo->pitch = -(int)(aim * ANGLE_180/M_PI);
}
//Checks if a sector is dangerous.
@ -388,4 +371,3 @@ bool FCajunMaster::IsDangerous (sector_t *sec)
|| special == Damage_InstantDeath
|| special == sDamage_SuperHellslime;
}

View File

@ -24,47 +24,49 @@ static FRandom pr_botmove ("BotMove");
//This function is called each tic for each bot,
//so this is what the bot does.
void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd)
void DBot::Think ()
{
ticcmd_t *cmd = &netcmds[player - players][((gametic + 1)/ticdup)%BACKUPTICS];
memset (cmd, 0, sizeof(*cmd));
if (actor->player->enemy && actor->player->enemy->health <= 0)
actor->player->enemy = NULL;
if (enemy && enemy->health <= 0)
enemy = NULL;
if (actor->health > 0) //Still alive
if (player->mo->health > 0) //Still alive
{
if (teamplay || !deathmatch)
actor->player->mate = Choose_Mate (actor);
mate = Choose_Mate ();
angle_t oldyaw = actor->angle;
int oldpitch = actor->pitch;
angle_t oldyaw = player->mo->angle;
int oldpitch = player->mo->pitch;
Set_enemy (actor);
ThinkForMove (actor, cmd);
TurnToAng (actor);
Set_enemy ();
ThinkForMove (cmd);
TurnToAng ();
cmd->ucmd.yaw = (short)((actor->angle - oldyaw) >> 16) / ticdup;
cmd->ucmd.pitch = (short)((oldpitch - actor->pitch) >> 16);
cmd->ucmd.yaw = (short)((player->mo->angle - oldyaw) >> 16) / ticdup;
cmd->ucmd.pitch = (short)((oldpitch - player->mo->pitch) >> 16);
if (cmd->ucmd.pitch == -32768)
cmd->ucmd.pitch = -32767;
cmd->ucmd.pitch /= ticdup;
actor->angle = oldyaw + (cmd->ucmd.yaw << 16) * ticdup;
actor->pitch = oldpitch - (cmd->ucmd.pitch << 16) * ticdup;
player->mo->angle = oldyaw + (cmd->ucmd.yaw << 16) * ticdup;
player->mo->pitch = oldpitch - (cmd->ucmd.pitch << 16) * ticdup;
}
if (actor->player->t_active) actor->player->t_active--;
if (actor->player->t_strafe) actor->player->t_strafe--;
if (actor->player->t_react) actor->player->t_react--;
if (actor->player->t_fight) actor->player->t_fight--;
if (actor->player->t_rocket) actor->player->t_rocket--;
if (actor->player->t_roam) actor->player->t_roam--;
if (t_active) t_active--;
if (t_strafe) t_strafe--;
if (t_react) t_react--;
if (t_fight) t_fight--;
if (t_rocket) t_rocket--;
if (t_roam) t_roam--;
//Respawn ticker
if (actor->player->t_respawn)
if (t_respawn)
{
actor->player->t_respawn--;
t_respawn--;
}
else if (actor->health <= 0)
else if (player->mo->health <= 0)
{ // Time to respawn
cmd->ucmd.buttons |= BT_USE;
}
@ -72,62 +74,57 @@ void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd)
//how the bot moves.
//MAIN movement function.
void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd)
void DBot::ThinkForMove (ticcmd_t *cmd)
{
player_t *b;
fixed_t dist;
bool stuck;
int r;
b = actor->player;
if (!b->isbot)
return;
stuck = false;
dist = b->dest ? P_AproxDistance(actor->x-b->dest->x, actor->y-b->dest->y) : 0;
dist = dest ? P_AproxDistance(player->mo->x-dest->x, player->mo->y-dest->y) : 0;
if (b->missile &&
((!b->missile->velx || !b->missile->vely) || !Check_LOS(actor, b->missile, SHOOTFOV*3/2)))
if (missile &&
((!missile->velx || !missile->vely) || !Check_LOS(missile, SHOOTFOV*3/2)))
{
b->sleft = !b->sleft;
b->missile = NULL; //Probably ended its travel.
sleft = !sleft;
missile = NULL; //Probably ended its travel.
}
if (actor->pitch > 0)
actor->pitch -= 80;
else if (actor->pitch <= -60)
actor->pitch += 80;
if (player->mo->pitch > 0)
player->mo->pitch -= 80;
else if (player->mo->pitch <= -60)
player->mo->pitch += 80;
//HOW TO MOVE:
if (b->missile && (P_AproxDistance(actor->x-b->missile->x, actor->y-b->missile->y)<AVOID_DIST)) //try avoid missile got from P_Mobj.c thinking part.
if (missile && (P_AproxDistance(player->mo->x-missile->x, player->mo->y-missile->y)<AVOID_DIST)) //try avoid missile got from P_Mobj.c thinking part.
{
Pitch (actor, b->missile);
actor->player->angle = R_PointToAngle2(actor->x, actor->y, b->missile->x, b->missile->y);
cmd->ucmd.sidemove = b->sleft ? -SIDERUN : SIDERUN;
Pitch (missile);
angle = R_PointToAngle2(player->mo->x, player->mo->y, missile->x, missile->y);
cmd->ucmd.sidemove = sleft ? -SIDERUN : SIDERUN;
cmd->ucmd.forwardmove = -FORWARDRUN; //Back IS best.
if ((P_AproxDistance(actor->x-b->oldx, actor->y-b->oldy)<50000)
&& b->t_strafe<=0)
if ((P_AproxDistance(player->mo->x-oldx, player->mo->y-oldy)<50000)
&& t_strafe<=0)
{
b->t_strafe = 5;
b->sleft = !b->sleft;
t_strafe = 5;
sleft = !sleft;
}
//If able to see enemy while avoiding missile, still fire at enemy.
if (b->enemy && Check_LOS (actor, b->enemy, SHOOTFOV))
Dofire (actor, cmd); //Order bot to fire current weapon
if (enemy && Check_LOS (enemy, SHOOTFOV))
Dofire (cmd); //Order bot to fire current weapon
}
else if (b->enemy && P_CheckSight (actor, b->enemy, 0)) //Fight!
else if (enemy && P_CheckSight (player->mo, enemy, 0)) //Fight!
{
Pitch (actor, b->enemy);
Pitch (enemy);
//Check if it's more important to get an item than fight.
if (b->dest && (b->dest->flags&MF_SPECIAL)) //Must be an item, that is close enough.
if (dest && (dest->flags&MF_SPECIAL)) //Must be an item, that is close enough.
{
#define is(x) b->dest->IsKindOf (PClass::FindClass (#x))
#define is(x) dest->IsKindOf (PClass::FindClass (#x))
if (
(
(actor->health < b->skill.isp &&
(player->mo->health < skill.isp &&
(is (Medikit) ||
is (Stimpack) ||
is (Soulsphere) ||
@ -140,78 +137,78 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd)
is (Megasphere)
) ||
dist < (GETINCOMBAT/4) ||
(b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)
(player->ReadyWeapon == NULL || player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)
)
&& (dist < GETINCOMBAT || (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
&& Reachable (actor, b->dest))
&& (dist < GETINCOMBAT || (player->ReadyWeapon == NULL || player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
&& Reachable (dest))
#undef is
{
goto roam; //Pick it up, no matter the situation. All bonuses are nice close up.
}
}
b->dest = NULL; //To let bot turn right
dest = NULL; //To let bot turn right
if (b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
actor->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting.
if (player->ReadyWeapon != NULL && !(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
player->mo->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting.
if (!(b->enemy->flags3 & MF3_ISMONSTER))
b->t_fight = AFTERTICS;
if (!(enemy->flags3 & MF3_ISMONSTER))
t_fight = AFTERTICS;
if (b->t_strafe <= 0 &&
(P_AproxDistance(actor->x-b->oldx, actor->y-b->oldy)<50000
if (t_strafe <= 0 &&
(P_AproxDistance(player->mo->x-oldx, player->mo->y-oldy)<50000
|| ((pr_botmove()%30)==10))
)
{
stuck = true;
b->t_strafe = 5;
b->sleft = !b->sleft;
t_strafe = 5;
sleft = !sleft;
}
b->angle = R_PointToAngle2(actor->x, actor->y, b->enemy->x, b->enemy->y);
angle = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y);
if (b->ReadyWeapon == NULL ||
P_AproxDistance(actor->x-b->enemy->x, actor->y-b->enemy->y) >
b->ReadyWeapon->MoveCombatDist)
if (player->ReadyWeapon == NULL ||
P_AproxDistance(player->mo->x-enemy->x, player->mo->y-enemy->y) >
player->ReadyWeapon->MoveCombatDist)
{
// If a monster, use lower speed (just for cooler apperance while strafing down doomed monster)
cmd->ucmd.forwardmove = (b->enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN;
cmd->ucmd.forwardmove = (enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN;
}
else if (!stuck) //Too close, so move away.
{
// If a monster, use lower speed (just for cooler apperance while strafing down doomed monster)
cmd->ucmd.forwardmove = (b->enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN;
cmd->ucmd.forwardmove = (enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN;
}
//Strafing.
if (b->enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool.
if (enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool.
{
cmd->ucmd.sidemove = b->sleft ? -SIDEWALK : SIDEWALK;
cmd->ucmd.sidemove = sleft ? -SIDEWALK : SIDEWALK;
}
else
{
cmd->ucmd.sidemove = b->sleft ? -SIDERUN : SIDERUN;
cmd->ucmd.sidemove = sleft ? -SIDERUN : SIDERUN;
}
Dofire (actor, cmd); //Order bot to fire current weapon
Dofire (cmd); //Order bot to fire current weapon
}
else if (b->mate && !b->enemy && (!b->dest || b->dest==b->mate)) //Follow mate move.
else if (mate && !enemy && (!dest || dest==mate)) //Follow mate move.
{
fixed_t matedist;
Pitch (actor, b->mate);
Pitch (mate);
if (!Reachable (actor, b->mate))
if (!Reachable (mate))
{
if (b->mate == b->dest && pr_botmove.Random() < 32)
if (mate == dest && pr_botmove.Random() < 32)
{ // [RH] If the mate is the dest, pick a new dest sometimes
b->dest = NULL;
dest = NULL;
}
goto roam;
}
actor->player->angle = R_PointToAngle2(actor->x, actor->y, b->mate->x, b->mate->y);
angle = R_PointToAngle2(player->mo->x, player->mo->y, mate->x, mate->y);
matedist = P_AproxDistance(actor->x - b->mate->x, actor->y - b->mate->y);
matedist = P_AproxDistance(player->mo->x - mate->x, player->mo->y - mate->y);
if (matedist > (FRIEND_DIST*2))
cmd->ucmd.forwardmove = FORWARDRUN;
else if (matedist > FRIEND_DIST)
@ -221,42 +218,42 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd)
}
else //Roam after something.
{
b->first_shot = true;
first_shot = true;
/////
roam:
/////
if (b->enemy && Check_LOS (actor, b->enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it.
Dofire (actor, cmd); //Order bot to fire current weapon
if (enemy && Check_LOS (enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it.
Dofire (cmd); //Order bot to fire current weapon
if (b->dest && !(b->dest->flags&MF_SPECIAL) && b->dest->health < 0)
if (dest && !(dest->flags&MF_SPECIAL) && dest->health < 0)
{ //Roaming after something dead.
b->dest = NULL;
dest = NULL;
}
if (b->dest == NULL)
if (dest == NULL)
{
if (b->t_fight && b->enemy) //Enemy/bot has jumped around corner. So what to do?
if (t_fight && enemy) //Enemy/bot has jumped around corner. So what to do?
{
if (b->enemy->player)
if (enemy->player)
{
if (((b->enemy->player->ReadyWeapon != NULL && b->enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) ||
(pr_botmove()%100)>b->skill.isp) && b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
b->dest = b->enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy.
else //hide while b->t_fight, but keep view at enemy.
b->angle = R_PointToAngle2(actor->x, actor->y, b->enemy->x, b->enemy->y);
if (((enemy->player->ReadyWeapon != NULL && enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) ||
(pr_botmove()%100)>skill.isp) && player->ReadyWeapon != NULL && !(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON))
dest = enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy.
else //hide while t_fight, but keep view at enemy.
angle = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y);
} //Just a monster, so kill it.
else
b->dest = b->enemy;
dest = enemy;
//VerifFavoritWeapon(actor->player); //Dont know why here.., but it must be here, i know the reason, but not why at this spot, uh.
//VerifFavoritWeapon(player); //Dont know why here.., but it must be here, i know the reason, but not why at this spot, uh.
}
else //Choose a distant target. to get things going.
{
r = pr_botmove();
if (r < 128)
{
TThinkerIterator<AInventory> it (STAT_INVENTORY, firstthing);
TThinkerIterator<AInventory> it (STAT_INVENTORY, bglobal.firstthing);
AInventory *item = it.Next();
if (item != NULL || (item = it.Next()) != NULL)
@ -271,60 +268,53 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd)
{
item = it.Next();
}
firstthing = item;
b->dest = item;
bglobal.firstthing = item;
dest = item;
}
}
else if (b->mate && (r < 179 || P_CheckSight(actor, b->mate)))
else if (mate && (r < 179 || P_CheckSight(player->mo, mate)))
{
b->dest = b->mate;
dest = mate;
}
else if ((playeringame[(r&(MAXPLAYERS-1))]) && players[(r&(MAXPLAYERS-1))].mo->health > 0)
{
b->dest = players[(r&(MAXPLAYERS-1))].mo;
dest = players[(r&(MAXPLAYERS-1))].mo;
}
}
if (b->dest)
if (dest)
{
b->t_roam = MAXROAM;
t_roam = MAXROAM;
}
}
if (b->dest)
if (dest)
{ //Bot has a target so roam after it.
Roam (actor, cmd);
Roam (cmd);
}
} //End of movement main part.
if (!b->t_roam && b->dest)
if (!t_roam && dest)
{
b->prev = b->dest;
b->dest = NULL;
prev = dest;
dest = NULL;
}
if (b->t_fight<(AFTERTICS/2))
actor->flags |= MF_DROPOFF;
if (t_fight<(AFTERTICS/2))
player->mo->flags |= MF_DROPOFF;
b->oldx = actor->x;
b->oldy = actor->y;
oldx = player->mo->x;
oldy = player->mo->y;
}
//BOT_WhatToGet
//
//Determines if the bot will roam after an item or not.
void FCajunMaster::WhatToGet (AActor *actor, AActor *item)
void DBot::WhatToGet (AActor *item)
{
player_t *b = actor->player;
if (b == NULL)
{
return;
}
#define typeis(x) item->IsKindOf (PClass::FindClass (#x))
if ((item->renderflags & RF_INVISIBLE) //Under respawn and away.
|| item == b->prev)
|| item == prev)
{
return;
}
@ -338,7 +328,7 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item)
// FIXME
AWeapon *heldWeapon;
heldWeapon = static_cast<AWeapon *> (b->mo->FindInventory (item->GetClass()));
heldWeapon = dyn_cast<AWeapon>(player->mo->FindInventory(item->GetClass()));
if (heldWeapon != NULL)
{
if (!weapgiveammo)
@ -354,39 +344,38 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item)
{
AAmmo *ammo = static_cast<AAmmo *> (item);
PClassActor *parent = ammo->GetParentAmmo ();
AInventory *holdingammo = b->mo->FindInventory (parent);
AInventory *holdingammo = player->mo->FindInventory (parent);
if (holdingammo != NULL && holdingammo->Amount >= holdingammo->MaxAmount)
{
return;
}
}
else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && actor->health >= deh.MaxSoulsphere)
else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere)
return;
else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && actor->health >= deh.MaxHealth /*MAXHEALTH*/)
else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && player->mo->health >= deh.MaxHealth /*MAXHEALTH*/)
return;
if ((b->dest == NULL ||
!(b->dest->flags & MF_SPECIAL)/* ||
!Reachable (actor, b->dest)*/)/* &&
Reachable (actor, item)*/) // Calling Reachable slows this down tremendously
if ((dest == NULL ||
!(dest->flags & MF_SPECIAL)/* ||
!Reachable (dest)*/)/* &&
Reachable (item)*/) // Calling Reachable slows this down tremendously
{
b->prev = b->dest;
b->dest = item;
b->t_roam = MAXROAM;
prev = dest;
dest = item;
t_roam = MAXROAM;
}
}
void FCajunMaster::Set_enemy (AActor *actor)
void DBot::Set_enemy ()
{
AActor *oldenemy;
AActor **enemy = &actor->player->enemy;
if (*enemy
&& (*enemy)->health > 0
&& P_CheckSight (actor, *enemy))
if (enemy
&& enemy->health > 0
&& P_CheckSight (player->mo, enemy))
{
oldenemy = *enemy;
oldenemy = enemy;
}
else
{
@ -395,15 +384,14 @@ void FCajunMaster::Set_enemy (AActor *actor)
// [RH] Don't even bother looking for a different enemy if this is not deathmatch
// and we already have an existing enemy.
if (deathmatch || !*enemy)
if (deathmatch || !enemy)
{
actor->player->allround = !!*enemy;
*enemy = NULL;
*enemy = Find_enemy(actor);
if (!*enemy)
*enemy = oldenemy; //Try go for last (it will be NULL if there wasn't anyone)
allround = !!enemy;
enemy = Find_enemy();
if (!enemy)
enemy = oldenemy; //Try go for last (it will be NULL if there wasn't anyone)
}
//Verify that that enemy is really something alive that bot can kill.
if (*enemy && (((*enemy)->health < 0 || !((*enemy)->flags&MF_SHOOTABLE)) || actor->IsFriend(*enemy)))
*enemy = NULL;
if (enemy && ((enemy->health < 0 || !(enemy->flags&MF_SHOOTABLE)) || player->mo->IsFriend(enemy)))
enemy = NULL;
}

View File

@ -230,7 +230,11 @@ const char *KeyNames[NUM_KEYS] =
NULL, NULL, NULL, NULL, NULL, "pause", NULL, "home", //C0
"uparrow", "pgup", NULL, "leftarrow",NULL, "rightarrow",NULL, "end", //C8
"downarrow","pgdn", "ins", "del", NULL, NULL, NULL, NULL, //D0
#ifdef __APPLE__
NULL, NULL, NULL, "command", NULL, "apps", "power", "sleep", //D8
#else // !__APPLE__
NULL, NULL, NULL, "lwin", "rwin", "apps", "power", "sleep", //D8
#endif // __APPLE__
NULL, NULL, NULL, "wake", NULL, "search", "favorites","refresh", //E0
"webstop", "webforward","webback", "mycomputer","mail", "mediaselect",NULL, NULL, //E8
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //F0

View File

@ -122,6 +122,15 @@ CCMD (god)
Net_WriteByte (CHT_GOD);
}
CCMD(god2)
{
if (CheckCheatmode())
return;
Net_WriteByte(DEM_GENERICCHEAT);
Net_WriteByte(CHT_GOD2);
}
CCMD (iddqd)
{
if (CheckCheatmode ())
@ -140,6 +149,15 @@ CCMD (buddha)
Net_WriteByte(CHT_BUDDHA);
}
CCMD(buddha2)
{
if (CheckCheatmode())
return;
Net_WriteByte(DEM_GENERICCHEAT);
Net_WriteByte(CHT_BUDDHA2);
}
CCMD (notarget)
{
if (CheckCheatmode ())

View File

@ -561,6 +561,11 @@ int PrintString (int printlevel, const char *outline)
maybedrawnow (false, false);
}
}
else if (Logfile != NULL)
{
fputs (outline, Logfile);
fflush (Logfile);
}
return (int)strlen (outline);
}
@ -1421,7 +1426,11 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
case 'V':
TabbedLast = false;
TabbedList = false;
#ifdef __APPLE__
if (ev->data3 & GKM_META)
#else // !__APPLE__
if (ev->data3 & GKM_CTRL)
#endif // __APPLE__
{
if (data1 == 'C')
{ // copy to clipboard
@ -1545,13 +1554,6 @@ void C_MidPrint (FFont *font, const char *msg)
AddToConsole (-1, bar1);
AddToConsole (-1, msg);
AddToConsole (-1, bar3);
if (Logfile)
{
fputs (logbar, Logfile);
fputs (msg, Logfile);
fputs (logbar, Logfile);
fflush (Logfile);
}
StatusBar->AttachMessage (new DHUDMessage (font, msg, 1.5f, 0.375f, 0, 0,
(EColorRange)PrintColors[PRINTLEVELS], con_midtime), MAKE_ID('C','N','T','R'));
@ -1569,13 +1571,6 @@ void C_MidPrintBold (FFont *font, const char *msg)
AddToConsole (-1, bar2);
AddToConsole (-1, msg);
AddToConsole (-1, bar3);
if (Logfile)
{
fputs (logbar, Logfile);
fputs (msg, Logfile);
fputs (logbar, Logfile);
fflush (Logfile);
}
StatusBar->AttachMessage (new DHUDMessage (font, msg, 1.5f, 0.375f, 0, 0,
(EColorRange)PrintColors[PRINTLEVELS+1], con_midtime), MAKE_ID('C','N','T','R'));

View File

@ -500,9 +500,10 @@ UCVarValue FBaseCVar::FromString (const char *value, ECVarType type)
goodv = false;
break;
default:
if (value[i] < '0' && value[i] > '9' &&
value[i] < 'A' && value[i] > 'F' &&
value[i] < 'a' && value[i] > 'f')
if (value[i] < '0' ||
(value[i] > '9' && value[i] < 'A') ||
(value[i] > 'F' && value[i] < 'a') ||
value[i] > 'f')
{
goodv = false;
}
@ -1514,6 +1515,22 @@ void UnlatchCVars (void)
}
}
void DestroyCVarsFlagged (DWORD flags)
{
FBaseCVar *cvar = CVars;
FBaseCVar *next = cvar;
while(cvar)
{
next = cvar->m_Next;
if(cvar->Flags & flags)
delete cvar;
cvar = next;
}
}
void C_SetCVarsToDefaults (void)
{
FBaseCVar *cvar = CVars;

View File

@ -159,6 +159,7 @@ private:
friend FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev);
friend FBaseCVar *FindCVarSub (const char *var_name, int namelen);
friend void UnlatchCVars (void);
friend void DestroyCVarsFlagged (DWORD flags);
friend void C_ArchiveCVars (FConfigFile *f, uint32 filter);
friend void C_SetCVarsToDefaults (void);
friend void FilterCompactCVars (TArray<FBaseCVar *> &cvars, uint32 filter);
@ -190,6 +191,9 @@ FBaseCVar *C_CreateCVar(const char *var_name, ECVarType var_type, DWORD flags);
// Called from G_InitNew()
void UnlatchCVars (void);
// Destroy CVars with the matching flags; called from CCMD(restart)
void DestroyCVarsFlagged (DWORD flags);
// archive cvars to FILE f
void C_ArchiveCVars (FConfigFile *f, uint32 filter);

View File

@ -723,7 +723,7 @@ void AddCommandString (char *cmd, int keynum)
// Note that deferred commands lose track of which key
// (if any) they were pressed from.
*brkpt = ';';
new DWaitingCommand (brkpt, tics+1);
new DWaitingCommand (brkpt, tics);
}
return;
}

View File

@ -5,6 +5,8 @@
#include "doomtype.h"
#include "doomdef.h"
#include "m_fixed.h"
#include <stdio.h>
#include <string.h>
@ -62,4 +64,25 @@ struct FFileList
void ScanDirectory(TArray<FFileList> &list, const char *dirpath);
//==========================================================================
//
// Functions to compensate for a tic being a bit short.
// Since ZDoom uses a milliseconds timer for game timing
// 35 tics are actually only 0.98 seconds.
// For real time display this needs to be adjusted
//
//==========================================================================
inline int AdjustTics(int tics)
{
return Scale(tics, 98, 100);
}
inline int Tics2Seconds(int tics)
{
return Scale(tics, 98, (100 * TICRATE));
}
#endif

View File

@ -0,0 +1,926 @@
// File: HID_Config_Utilities.c
// Abstract: Implementation of the HID configuration utilities
// Version: 2.0
//
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
// Inc. ("Apple") in consideration of your agreement to the following
// terms, and your use, installation, modification or redistribution of
// this Apple software constitutes acceptance of these terms. If you do
// not agree with these terms, please do not use, install, modify or
// redistribute this Apple software.
//
// In consideration of your agreement to abide by the following terms, and
// subject to these terms, Apple grants you a personal, non-exclusive
// license, under Apple's copyrights in this original Apple software (the
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
// Software, with or without modifications, in source and/or binary forms;
// provided that if you redistribute the Apple Software in its entirety and
// without modifications, you must retain this notice and the following
// text and disclaimers in all such redistributions of the Apple Software.
// Neither the name, trademarks, service marks or logos of Apple Inc. may
// be used to endorse or promote products derived from the Apple Software
// without specific prior written permission from Apple. Except as
// expressly stated in this notice, no other rights or licenses, express or
// implied, are granted by Apple herein, including but not limited to any
// patent rights that may be infringed by your derivative works or by other
// works in which the Apple Software may be incorporated.
//
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (C) 2009 Apple Inc. All Rights Reserved.
//
//*****************************************************
#include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
#define LOG_SCORING 0
#include <stdlib.h> // malloc
#include <time.h> // clock
#include <AssertMacros.h>
#include "HID_Utilities_External.h"
// ---------------------------------
// polls single device's elements for a change greater than kPercentMove. Times out after given time
// returns 1 and pointer to element if found
// returns 0 and NULL for both parameters if not found
unsigned char HIDConfigureSingleDeviceAction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef, float timeout) {
if ( !inIOHIDDeviceRef ) {
return (0);
}
if ( 0 == HIDHaveDeviceList() ) { // if we do not have a device list
return (0); // return 0
}
Boolean found = FALSE;
// build list of device and elements to save current values
int maxElements = HIDCountDeviceElements(inIOHIDDeviceRef, kHIDElementTypeInput);
int *saveValueArray = (int *) calloc( maxElements, sizeof(int) ); // 2D array to save values
// store initial values on first pass / compare to initial value on subsequent passes
Boolean first = TRUE;
// get all the elements from this device
CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(inIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone);
// if that worked...
if ( elementCFArrayRef ) {
clock_t start = clock(), end;
// poll all devices and elements
while ( !found ) {
int currElementIndex = 0;
CFIndex idx, cnt = CFArrayGetCount(elementCFArrayRef);
for ( idx = 0; idx < cnt; idx++ ) {
*outIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx);
if ( !*outIOHIDElementRef ) {
continue;
}
// is this an input element?
IOHIDElementType type = IOHIDElementGetType(*outIOHIDElementRef);
switch ( type ) {
// these types are inputs
case kIOHIDElementTypeInput_Misc:
case kIOHIDElementTypeInput_Button:
case kIOHIDElementTypeInput_Axis:
case kIOHIDElementTypeInput_ScanCodes:
default:
{
break;
}
case kIOHIDElementTypeOutput:
case kIOHIDElementTypeFeature:
case kIOHIDElementTypeCollection:
{
*outIOHIDElementRef = NULL; // these types are not ( Skip them )
break;
}
} /* switch */
if ( !*outIOHIDElementRef ) {
continue; // skip this element
}
// get this elements current value
int value = 0; // default value is zero
IOHIDValueRef tIOHIDValueRef;
IOReturn ioReturn = IOHIDDeviceGetValue(inIOHIDDeviceRef, *outIOHIDElementRef, &tIOHIDValueRef);
if ( kIOReturnSuccess == ioReturn ) {
value = IOHIDValueGetScaledValue(tIOHIDValueRef, kIOHIDValueScaleTypePhysical);
}
if ( first ) {
saveValueArray[currElementIndex] = value;
} else {
CFIndex min = IOHIDElementGetLogicalMin(*outIOHIDElementRef);
CFIndex max = IOHIDElementGetLogicalMax(*outIOHIDElementRef);
int initialValue = saveValueArray[currElementIndex];
int delta = (float)(max - min) * kPercentMove * 0.01;
// is the new value within +/- delta of the initial value?
if ( ( (initialValue + delta) < value ) || ( (initialValue - delta) > value ) ) {
found = 1; // ( yes! ) mark as found
break;
}
} // if ( first )
currElementIndex++; // bump element index
} // next idx
first = FALSE; // no longer the first pass
// are we done?
end = clock();
double secs = (double)(end - start) / CLOCKS_PER_SEC;
if ( secs > timeout ) {
break; // ( yes ) timeout
}
} // while ( !found )
CFRelease(elementCFArrayRef);
} // if ( elementCFArrayRef )
// return device and element moved
if ( found ) {
return (1);
} else {
*outIOHIDElementRef = NULL;
return (0);
}
} // HIDConfigureSingleDeviceAction
//*************************************************************************
//
// HIDConfigureAction( outIOHIDDeviceRef, outIOHIDElementRef, inTimeout )
//
// Purpose: polls all devices and elements for a change greater than kPercentMove.
// Times out after given time returns 1 and pointer to device and element
// if found; returns 0 and NULL for both parameters if not found
//
// Inputs: outIOHIDDeviceRef - address where to store the device
// outIOHIDElementRef - address where to store the element
// inTimeout - the timeout
// Returns: Boolean - if successful
// outIOHIDDeviceRef - the device
// outIOHIDElementRef - the element
//
Boolean HIDConfigureAction(IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef, float inTimeout) {
// param error?
if ( !outIOHIDDeviceRef || !outIOHIDElementRef ) {
return (0);
}
if ( !gDeviceCFArrayRef ) { // if we do not have a device list
// and we can't build another list
if ( !HIDBuildDeviceList(0, 0) || !gDeviceCFArrayRef ) {
return (FALSE); // bail
}
}
IOHIDDeviceRef tIOHIDDeviceRef;
IOHIDElementRef tIOHIDElementRef;
// remember when we start; used to calculate timeout
clock_t start = clock(), end;
// determine the maximum number of elements
CFIndex maxElements = 0;
CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef);
for ( devIndex = 0; devIndex < devCount; devIndex++ ) {
tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex);
if ( !tIOHIDDeviceRef ) {
continue; // skip this one
}
CFIndex count = HIDCountDeviceElements(tIOHIDDeviceRef, kHIDElementTypeInput);
if ( count > maxElements ) {
maxElements = count;
}
}
// allocate an array of int's in which to store devCount * maxElements values
double *saveValueArray = (double *) calloc( devCount * maxElements, sizeof(double) ); // clear 2D array to save values
// on first pass store initial values / compare current values to initial values on subsequent passes
Boolean found = FALSE, first = TRUE;
while ( !found ) {
double maxDeltaPercent = 0; // we want to find the one that moves the most ( percentage wise )
for ( devIndex = 0; devIndex < devCount; devIndex++ ) {
tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex);
if ( !tIOHIDDeviceRef ) {
continue; // skip this one
}
#ifdef DEBUG
long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef);
long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef);
#endif
gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone);
if ( gElementCFArrayRef ) {
CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef);
for ( eleIndex = 0; eleIndex < eleCount; eleIndex++ ) {
tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex);
if ( !tIOHIDElementRef ) {
continue;
}
IOHIDElementType tIOHIDElementType = IOHIDElementGetType(tIOHIDElementRef);
// only care about inputs (no outputs or features)
if ( tIOHIDElementType <= kIOHIDElementTypeInput_ScanCodes ) {
if ( IOHIDElementIsArray(tIOHIDElementRef) ) {
//printf( "ARRAY!\n" );
continue; // skip array elements
}
uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef);
uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef);
uint32_t reportCount = IOHIDElementGetReportCount(tIOHIDElementRef);
#ifdef DEBUG
if ( first ) {
IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef);
printf("%s, dev: {ref:%p, ven: 0x%08lX, pro: 0x%08lX}, ele: {ref:%p, cookie: %p, usage:%04lX:%08lX}\n",
__PRETTY_FUNCTION__,
tIOHIDDeviceRef,
vendorID,
productID,
tIOHIDElementRef,
cookie,
(long unsigned int) usagePage,
(long unsigned int) usage);
fflush(stdout);
if ( (0x054C == vendorID) && (0x0268 == productID) && (0x001E == (UInt32) cookie) ) {
//printf( "DING!\n" );
}
}
#endif
#if 1 // work-around for IOHIDValueGetScaledValue crash (when element report count > 1)
if ( reportCount > 1 ) {
//printf( "REPORT!\n" );
continue; // skip reports
}
#endif
// ignore PID elements and arrays
if ( (kHIDPage_PID != usagePage) && (((uint32_t)-1) != usage) ) {
// get this elements current value
double value = 0.0; // default value is zero
IOHIDValueRef tIOHIDValueRef;
IOReturn ioReturn = IOHIDDeviceGetValue(tIOHIDDeviceRef, tIOHIDElementRef, &tIOHIDValueRef);
if ( kIOReturnSuccess == ioReturn ) {
value = IOHIDValueGetScaledValue(tIOHIDValueRef, kIOHIDValueScaleTypePhysical);
}
if ( first ) {
saveValueArray[(devIndex * maxElements) + eleIndex] = value;
} else {
double initialValue = saveValueArray[(devIndex * maxElements) + eleIndex];
CFIndex valueMin = IOHIDElementGetPhysicalMin(tIOHIDElementRef);
CFIndex valueMax = IOHIDElementGetPhysicalMax(tIOHIDElementRef);
double deltaPercent = fabs( (initialValue - value) * 100.0 / (valueMax - valueMin) );
#if 0 // debug code useful to dump out value info for specific (vendorID, productID, usagePage and usage) device
if ( !first ) {
// Device: 0x13b6a0 = { Logitech Inc. - WingMan Force 3D, vendorID: 0x046D, productID: 0xC283,
// usage: 0x0001:0x0004, "Generic Desktop Joystick"
if ( (vendorID == 0x046D) && (productID == 0xC283) ) {
if ( (kHIDPage_GenericDesktop == usagePage) && (kHIDUsage_GD_Rz == usage) ) {
printf("initial: %6.2f, value: %6.2f, diff: %6.2f, delta percent: %6.2f!\n",
initialValue,
value,
fabs(initialValue - value),
deltaPercent);
}
}
}
deltaPercent = 0.0;
#endif
if ( deltaPercent >= kPercentMove ) {
found = TRUE;
if ( deltaPercent > maxDeltaPercent ) {
maxDeltaPercent = deltaPercent;
*outIOHIDDeviceRef = tIOHIDDeviceRef;
*outIOHIDElementRef = tIOHIDElementRef;
}
break;
}
} // if first
} // if usage
} // if type
} // for elements...
CFRelease(gElementCFArrayRef);
gElementCFArrayRef = NULL;
} // if ( gElementCFArrayRef )
if ( found ) {
// HIDDumpElementInfo( tIOHIDElementRef );
break; // DONE!
}
} // for devices
first = FALSE; // no longer the first pass
// are we done?
end = clock();
double secs = (double)(end - start) / CLOCKS_PER_SEC;
if ( secs > inTimeout ) {
break; // ( yes ) timeout
}
} // while ( !found )
// return device and element moved
if ( !found ) {
*outIOHIDDeviceRef = NULL;
*outIOHIDElementRef = NULL;
}
return (found);
} // HIDConfigureAction
//*************************************************************************
//
// HIDSaveElementPref( inKeyCFStringRef, inAppCFStringRef, inIOHIDDeviceRef, inIOHIDElementRef )
//
// Purpose: Save the device & element values into the specified key in the specified applications preferences
//
// Inputs: inKeyCFStringRef - the preference key
// inAppCFStringRef - the application identifier
// inIOHIDDeviceRef - the device
// inIOHIDElementRef - the element
// Returns: Boolean - if successful
//
Boolean HIDSaveElementPref(const CFStringRef inKeyCFStringRef,
CFStringRef inAppCFStringRef,
IOHIDDeviceRef inIOHIDDeviceRef,
IOHIDElementRef inIOHIDElementRef) {
Boolean success = FALSE;
if ( inKeyCFStringRef && inAppCFStringRef && inIOHIDDeviceRef && inIOHIDElementRef ) {
long vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef);
require(vendorID, Oops);
long productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef);
require(productID, Oops);
long locID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef);
require(locID, Oops);
uint32_t usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef);
uint32_t usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef);
if ( !usagePage || !usage ) {
usagePage = IOHIDDevice_GetPrimaryUsagePage(inIOHIDDeviceRef);
usage = IOHIDDevice_GetPrimaryUsage(inIOHIDDeviceRef);
}
require(usagePage && usage, Oops);
uint32_t usagePageE = IOHIDElementGetUsagePage(inIOHIDElementRef);
uint32_t usageE = IOHIDElementGetUsage(inIOHIDElementRef);
IOHIDElementCookie eleCookie = IOHIDElementGetCookie(inIOHIDElementRef);
CFStringRef prefCFStringRef = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR("d:{v:%ld, p:%ld, l:%ld, p:%ld, u:%ld}, e:{p:%ld, u:%ld, c:%ld}"),
vendorID, productID, locID, usagePage, usage,
usagePageE, usageE, eleCookie);
if ( prefCFStringRef ) {
CFPreferencesSetAppValue(inKeyCFStringRef, prefCFStringRef, inAppCFStringRef);
CFRelease(prefCFStringRef);
success = TRUE;
}
}
Oops: ;
return (success);
} // HIDSaveElementPref
//*************************************************************************
//
// HIDRestoreElementPref( inKeyCFStringRef, inAppCFStringRef, outIOHIDDeviceRef, outIOHIDElementRef )
//
// Purpose: Find the specified preference in the specified application
//
// Inputs: inKeyCFStringRef - the preference key
// inAppCFStringRef - the application identifier
// outIOHIDDeviceRef - address where to restore the device
// outIOHIDElementRef - address where to restore the element
// Returns: Boolean - if successful
// outIOHIDDeviceRef - the device
// outIOHIDElementRef - the element
//
Boolean HIDRestoreElementPref(CFStringRef inKeyCFStringRef,
CFStringRef inAppCFStringRef,
IOHIDDeviceRef * outIOHIDDeviceRef,
IOHIDElementRef *outIOHIDElementRef) {
Boolean found = FALSE;
if ( inKeyCFStringRef && inAppCFStringRef && outIOHIDDeviceRef && outIOHIDElementRef ) {
CFPropertyListRef prefCFPropertyListRef = CFPreferencesCopyAppValue(inKeyCFStringRef, inAppCFStringRef);
if ( prefCFPropertyListRef ) {
if ( CFStringGetTypeID() == CFGetTypeID(prefCFPropertyListRef) ) {
char buffer[256];
if ( CFStringGetCString( (CFStringRef) prefCFPropertyListRef, buffer, sizeof(buffer),
kCFStringEncodingUTF8 ) )
{
HID_info_rec searchHIDInfo;
int count = sscanf(buffer,
"d:{v:%d, p:%d, l:%d, p:%d, u:%d}, e:{p:%d, u:%d, c:%ld}",
&searchHIDInfo.device.vendorID,
&searchHIDInfo.device.productID,
&searchHIDInfo.device.locID,
&searchHIDInfo.device.usagePage,
&searchHIDInfo.device.usage,
&searchHIDInfo.element.usagePage,
&searchHIDInfo.element.usage,
(long *) &searchHIDInfo.element.cookie);
if ( 8 == count ) { // if we found all eight parameters…
// and can find a device & element that matches these…
if ( HIDFindDeviceAndElement(&searchHIDInfo, outIOHIDDeviceRef, outIOHIDElementRef) ) {
found = TRUE;
}
}
}
} else {
// We found the entry with this key but it's the wrong type; delete it.
CFPreferencesSetAppValue(inKeyCFStringRef, NULL, inAppCFStringRef);
(void) CFPreferencesAppSynchronize(inAppCFStringRef);
}
CFRelease(prefCFPropertyListRef);
}
}
return (found);
} // HIDRestoreElementPref
//*************************************************************************
//
// HIDFindDeviceAndElement( inSearchInfo, outFoundDevice, outFoundElement )
//
// Purpose: find the closest matching device and element for this action
//
// Notes: matches device: serial, vendorID, productID, location, inUsagePage, usage
// matches element: cookie, inUsagePage, usage,
//
// Inputs: inSearchInfo - the device & element info we searching for
// outFoundDevice - the address of the best matching device
// outFoundElement - the address of the best matching element
//
// Returns: Boolean - TRUE if we find a match
// outFoundDevice - the best matching device
// outFoundElement - the best matching element
//
Boolean HIDFindDeviceAndElement(const HID_info_rec *inSearchInfo, IOHIDDeviceRef *outFoundDevice, IOHIDElementRef *outFoundElement) {
Boolean result = FALSE;
IOHIDDeviceRef bestIOHIDDeviceRef = NULL;
IOHIDElementRef bestIOHIDElementRef = NULL;
long bestScore = 0;
CFIndex devIndex, devCount = CFArrayGetCount(gDeviceCFArrayRef);
for ( devIndex = 0; devIndex < devCount; devIndex++ ) {
long deviceScore = 1;
IOHIDDeviceRef tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIndex);
if ( !tIOHIDDeviceRef ) {
continue;
}
// match vendorID, productID (+10, +8)
if ( inSearchInfo->device.vendorID ) {
long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef);
if ( vendorID ) {
if ( inSearchInfo->device.vendorID == vendorID ) {
deviceScore += 10;
if ( inSearchInfo->device.productID ) {
long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef);
if ( productID ) {
if ( inSearchInfo->device.productID == productID ) {
deviceScore += 8;
} // if ( inSearchInfo->device.productID == productID )
} // if ( productID )
} // if ( inSearchInfo->device.productID )
} // if (inSearchInfo->device.vendorID == vendorID)
} // if vendorID
} // if search->device.vendorID
// match usagePage & usage (+9)
if ( inSearchInfo->device.usagePage && inSearchInfo->device.usage ) {
uint32_t usagePage = IOHIDDevice_GetUsagePage(tIOHIDDeviceRef) ;
uint32_t usage = IOHIDDevice_GetUsage(tIOHIDDeviceRef);
if ( !usagePage || !usage ) {
usagePage = IOHIDDevice_GetPrimaryUsagePage(tIOHIDDeviceRef);
usage = IOHIDDevice_GetPrimaryUsage(tIOHIDDeviceRef);
}
if ( usagePage ) {
if ( inSearchInfo->device.usagePage == usagePage ) {
if ( usage ) {
if ( inSearchInfo->device.usage == usage ) {
deviceScore += 9;
} // if ( inSearchInfo->usage == usage )
} // if ( usage )
} // if ( inSearchInfo->usagePage == usagePage )
} // if ( usagePage )
} // if ( inSearchInfo->usagePage && inSearchInfo->usage )
// match location ID (+5)
if ( inSearchInfo->device.locID ) {
long locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef);
if ( locID ) {
if ( inSearchInfo->device.locID == locID ) {
deviceScore += 5;
}
}
}
// iterate over all elements of this device
gElementCFArrayRef = IOHIDDeviceCopyMatchingElements(tIOHIDDeviceRef, NULL, 0);
if ( gElementCFArrayRef ) {
CFIndex eleIndex, eleCount = CFArrayGetCount(gElementCFArrayRef);
for ( eleIndex = 0; eleIndex < eleCount; eleIndex++ ) {
IOHIDElementRef tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(gElementCFArrayRef, eleIndex);
if ( !tIOHIDElementRef ) {
continue;
}
long score = deviceScore;
// match usage page, usage & cookie
if ( inSearchInfo->element.usagePage && inSearchInfo->element.usage ) {
uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef);
if ( inSearchInfo->element.usagePage == usagePage ) {
uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef);
if ( inSearchInfo->element.usage == usage ) {
score += 5;
IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef);
if ( inSearchInfo->element.cookie == cookie ) {
score += 4;
} // cookies match
} else {
score = 0;
} // usages match
} else {
score = 0;
} // usage pages match
} // if ( search usage page & usage )
#if LOG_SCORING
if ( kHIDPage_KeyboardOrKeypad != tElementRef->usagePage ) { // skip keyboards here
printf("%s: ( %ld:%ld )-I-Debug, score: %ld\t",
__PRETTY_FUNCTION__,
inSearchInfo->element.usagePage,
inSearchInfo->element.usage,
score);
HIDPrintElement(tIOHIDElementRef);
}
#endif // LOG_SCORING
if ( score > bestScore ) {
bestIOHIDDeviceRef = tIOHIDDeviceRef;
bestIOHIDElementRef = tIOHIDElementRef;
bestScore = score;
#if LOG_SCORING
printf("%s: ( %ld:%ld )-I-Debug, better score: %ld\t",
__PRETTY_FUNCTION__,
inSearchInfo->element.usagePage,
inSearchInfo->element.usage,
score);
HIDPrintElement(bestIOHIDElementRef);
#endif // LOG_SCORING
}
} // for elements...
CFRelease(gElementCFArrayRef);
gElementCFArrayRef = NULL;
} // if ( gElementCFArrayRef )
} // for ( devIndex = 0; devIndex < devCount; devIndex++ )
if ( bestIOHIDDeviceRef || bestIOHIDElementRef ) {
*outFoundDevice = bestIOHIDDeviceRef;
*outFoundElement = bestIOHIDElementRef;
#if LOG_SCORING
printf("%s: ( %ld:%ld )-I-Debug, best score: %ld\t",
__PRETTY_FUNCTION__,
inSearchInfo->element.usagePage,
inSearchInfo->element.usage,
bestScore);
HIDPrintElement(bestIOHIDElementRef);
#endif // LOG_SCORING
result = TRUE;
}
return (result);
} // HIDFindDeviceAndElement
// ---------------------------------
// takes input records, save required info
// assume file is open and at correct position.
// will always write to file (if file exists) size of HID_info_rec, even if device and or element is bad
void HIDSaveElementConfig(FILE *fileRef, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef, int actionCookie) {
// must save:
// actionCookie
// Device: serial,vendorID, productID, location, usagePage, usage
// Element: cookie, usagePage, usage,
HID_info_rec hidInfoRec;
HIDSetElementConfig(&hidInfoRec, inIOHIDDeviceRef, inIOHIDElementRef, actionCookie);
// write to file
if ( fileRef ) {
fwrite( (void *)&hidInfoRec, sizeof(HID_info_rec), 1, fileRef );
}
} // HIDSaveElementConfig
// ---------------------------------
// take file, read one record (assume file position is correct and file is open)
// search for matching device
// return pDevice, pElement and cookie for action
int HIDRestoreElementConfig(FILE *fileRef, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef) {
// Device: serial,vendorID, productID, location, usagePage, usage
// Element: cookie, usagePage, usage,
HID_info_rec hidInfoRec;
fread( (void *) &hidInfoRec, 1, sizeof(HID_info_rec), fileRef );
return ( HIDGetElementConfig(&hidInfoRec, outIOHIDDeviceRef, outIOHIDElementRef) );
} // HIDRestoreElementConfig
// ---------------------------------
// Set up a config record for saving
// takes an input records, returns record user can save as they want
// Note: the save rec must be pre-allocated by the calling app and will be filled out
void HIDSetElementConfig(HID_info_ptr inHIDInfoPtr,
IOHIDDeviceRef inIOHIDDeviceRef,
IOHIDElementRef inIOHIDElementRef,
int actionCookie) {
// must save:
// actionCookie
// Device: serial,vendorID, productID, location, usagePage, usage
// Element: cookie, usagePage, usage,
inHIDInfoPtr->actionCookie = actionCookie;
// device
// need to add serial number when I have a test case
if ( inIOHIDDeviceRef && inIOHIDElementRef ) {
inHIDInfoPtr->device.vendorID = IOHIDDevice_GetVendorID(inIOHIDDeviceRef);
inHIDInfoPtr->device.productID = IOHIDDevice_GetProductID(inIOHIDDeviceRef);
inHIDInfoPtr->device.locID = IOHIDDevice_GetLocationID(inIOHIDDeviceRef);
inHIDInfoPtr->device.usage = IOHIDDevice_GetUsage(inIOHIDDeviceRef);
inHIDInfoPtr->device.usagePage = IOHIDDevice_GetUsagePage(inIOHIDDeviceRef);
inHIDInfoPtr->element.usagePage = IOHIDElementGetUsagePage(inIOHIDElementRef);
inHIDInfoPtr->element.usage = IOHIDElementGetUsage(inIOHIDElementRef);
inHIDInfoPtr->element.minReport = IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef);
inHIDInfoPtr->element.maxReport = IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef);
inHIDInfoPtr->element.cookie = IOHIDElementGetCookie(inIOHIDElementRef);
} else {
inHIDInfoPtr->device.vendorID = 0;
inHIDInfoPtr->device.productID = 0;
inHIDInfoPtr->device.locID = 0;
inHIDInfoPtr->device.usage = 0;
inHIDInfoPtr->device.usagePage = 0;
inHIDInfoPtr->element.usagePage = 0;
inHIDInfoPtr->element.usage = 0;
inHIDInfoPtr->element.minReport = 0;
inHIDInfoPtr->element.maxReport = 0;
inHIDInfoPtr->element.cookie = 0;
}
} // HIDSetElementConfig
// ---------------------------------
#if 0 // debug utility function to dump config record
void HIDDumpConfig(HID_info_ptr inHIDInfoPtr) {
printf(
"Config Record for action: %d\n\t vendor: %d product: %d location: %d\n\t usage: %d usagePage: %d\n\t element.usagePage: %d element.usage: %d\n\t minReport: %d maxReport: %d\n\t cookie: %d\n",
inHIDInfoPtr->actionCookie,
inHIDInfoPtr->device.vendorID,
inHIDInfoPtr->device.productID,
inHIDInfoPtr->locID,
inHIDInfoPtr->usage,
inHIDInfoPtr->usagePage,
inHIDInfoPtr->element.usagePage,
inHIDInfoPtr->element.usage,
inHIDInfoPtr->minReport,
inHIDInfoPtr->maxReport,
inHIDInfoPtr->cookie);
} // HIDDumpConfig
#endif // 0
// ---------------------------------
// Get matching element from config record
// takes a pre-allocated and filled out config record
// search for matching device
// return pDevice, pElement and cookie for action
int HIDGetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef) {
if ( !inHIDInfoPtr->device.locID && !inHIDInfoPtr->device.vendorID && !inHIDInfoPtr->device.productID && !inHIDInfoPtr->device.usage
&& !inHIDInfoPtr->device.usagePage ) //
{ //
// early out
*outIOHIDDeviceRef = NULL;
*outIOHIDElementRef = NULL;
return (inHIDInfoPtr->actionCookie);
}
IOHIDDeviceRef tIOHIDDeviceRef, foundIOHIDDeviceRef = NULL;
IOHIDElementRef tIOHIDElementRef, foundIOHIDElementRef = NULL;
CFIndex devIdx, devCnt, idx, cnt;
// compare to current device list for matches
// look for device
if ( inHIDInfoPtr->device.locID && inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID ) { // look for specific device
// type plug in to same port
devCnt = CFArrayGetCount(gDeviceCFArrayRef);
for ( devIdx = 0; devIdx < devCnt; devIdx++ ) {
tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIdx);
if ( !tIOHIDDeviceRef ) {
continue; // skip this device
}
long locID = IOHIDDevice_GetLocationID(tIOHIDDeviceRef);
long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef);
long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef);
if ( (inHIDInfoPtr->device.locID == locID)
&& (inHIDInfoPtr->device.vendorID == vendorID)
&& (inHIDInfoPtr->device.productID == productID) )
{
foundIOHIDDeviceRef = tIOHIDDeviceRef;
}
if ( foundIOHIDDeviceRef ) {
break;
}
} // next devIdx
if ( foundIOHIDDeviceRef ) {
CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone);
if ( elementCFArrayRef ) {
cnt = CFArrayGetCount(elementCFArrayRef);
for ( idx = 0; idx < cnt; idx++ ) {
tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx);
if ( !tIOHIDElementRef ) {
continue; // skip this element
}
IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef);
if ( inHIDInfoPtr->element.cookie == cookie ) {
foundIOHIDElementRef = tIOHIDElementRef;
}
if ( foundIOHIDElementRef ) {
break;
}
}
if ( !foundIOHIDElementRef ) {
cnt = CFArrayGetCount(elementCFArrayRef);
for ( idx = 0; idx < cnt; idx++ ) {
tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx);
if ( !tIOHIDElementRef ) {
continue; // skip this element
}
uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef);
uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef);
if ( (inHIDInfoPtr->element.usage == usage) && (inHIDInfoPtr->element.usagePage == usagePage) ) {
foundIOHIDElementRef = tIOHIDElementRef;
}
if ( foundIOHIDElementRef ) {
break;
}
} // next idx
} // if ( !foundIOHIDElementRef )
if ( foundIOHIDElementRef ) { // if same device
// setup the calibration
IOHIDElement_SetupCalibration(tIOHIDElementRef);
IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef, inHIDInfoPtr->element.minReport);
IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef, inHIDInfoPtr->element.maxReport);
}
CFRelease(elementCFArrayRef);
} // if ( elementCFArrayRef )
} // if ( foundIOHIDDeviceRef )
// if we have not found a match, look at just vendor and product
if ( (!foundIOHIDDeviceRef) && (inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID) ) {
devCnt = CFArrayGetCount(gDeviceCFArrayRef);
for ( devIdx = 0; devIdx < devCnt; devIdx++ ) {
tIOHIDDeviceRef = (IOHIDDeviceRef) CFArrayGetValueAtIndex(gDeviceCFArrayRef, devIdx);
if ( !tIOHIDDeviceRef ) {
continue; // skip this device
}
long vendorID = IOHIDDevice_GetVendorID(tIOHIDDeviceRef);
long productID = IOHIDDevice_GetProductID(tIOHIDDeviceRef);
if ( (inHIDInfoPtr->device.vendorID == vendorID)
&& (inHIDInfoPtr->device.productID == productID) )
{
foundIOHIDDeviceRef = tIOHIDDeviceRef;
}
if ( foundIOHIDDeviceRef ) {
break;
}
}
// match elements by cookie since same device type
if ( foundIOHIDDeviceRef ) {
CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(foundIOHIDDeviceRef, NULL, kIOHIDOptionsTypeNone);
if ( elementCFArrayRef ) {
cnt = CFArrayGetCount(elementCFArrayRef);
for ( idx = 0; idx < cnt; idx++ ) {
tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx);
if ( !tIOHIDElementRef ) {
continue; // skip this element
}
IOHIDElementCookie cookie = IOHIDElementGetCookie(tIOHIDElementRef);
if ( inHIDInfoPtr->element.cookie == cookie ) {
foundIOHIDElementRef = tIOHIDElementRef;
}
if ( foundIOHIDElementRef ) {
break;
}
}
// if no cookie match (should NOT occur) match on usage
if ( !foundIOHIDElementRef ) {
cnt = CFArrayGetCount(elementCFArrayRef);
for ( idx = 0; idx < cnt; idx++ ) {
tIOHIDElementRef = (IOHIDElementRef) CFArrayGetValueAtIndex(elementCFArrayRef, idx);
if ( !tIOHIDElementRef ) {
continue; // skip this element
}
uint32_t usagePage = IOHIDElementGetUsagePage(tIOHIDElementRef);
uint32_t usage = IOHIDElementGetUsage(tIOHIDElementRef);
if ( (inHIDInfoPtr->element.usage == usage)
&& (inHIDInfoPtr->element.usagePage == usagePage) )
{
foundIOHIDElementRef = tIOHIDElementRef;
}
if ( foundIOHIDElementRef ) {
break;
}
} // next idx
} // if ( !foundIOHIDElementRef )
if ( foundIOHIDElementRef ) { // if same device
// setup the calibration
IOHIDElement_SetupCalibration(tIOHIDElementRef);
IOHIDElement_SetCalibrationSaturationMin(tIOHIDElementRef, inHIDInfoPtr->element.minReport);
IOHIDElement_SetCalibrationSaturationMax(tIOHIDElementRef, inHIDInfoPtr->element.maxReport);
}
CFRelease(elementCFArrayRef);
} // if ( elementCFArrayRef )
} // if ( foundIOHIDDeviceRef )
} // if ( device not found & vendorID & productID )
} // if ( inHIDInfoPtr->locID && inHIDInfoPtr->device.vendorID && inHIDInfoPtr->device.productID )
// can't find matching device return NULL, do not return first device
if ( (!foundIOHIDDeviceRef) || (!foundIOHIDElementRef) ) {
// no HID device
*outIOHIDDeviceRef = NULL;
*outIOHIDElementRef = NULL;
return (inHIDInfoPtr->actionCookie);
} else {
// HID device
*outIOHIDDeviceRef = foundIOHIDDeviceRef;
*outIOHIDElementRef = foundIOHIDElementRef;
return (inHIDInfoPtr->actionCookie);
}
} // HIDGetElementConfig
#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050

View File

@ -0,0 +1,108 @@
// File: HID_Error_Handler.c
// Abstract: Implementation of the HID utilities error handlers
// Version: 2.0
//
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
// Inc. ("Apple") in consideration of your agreement to the following
// terms, and your use, installation, modification or redistribution of
// this Apple software constitutes acceptance of these terms. If you do
// not agree with these terms, please do not use, install, modify or
// redistribute this Apple software.
//
// In consideration of your agreement to abide by the following terms, and
// subject to these terms, Apple grants you a personal, non-exclusive
// license, under Apple's copyrights in this original Apple software (the
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
// Software, with or without modifications, in source and/or binary forms;
// provided that if you redistribute the Apple Software in its entirety and
// without modifications, you must retain this notice and the following
// text and disclaimers in all such redistributions of the Apple Software.
// Neither the name, trademarks, service marks or logos of Apple Inc. may
// be used to endorse or promote products derived from the Apple Software
// without specific prior written permission from Apple. Except as
// expressly stated in this notice, no other rights or licenses, express or
// implied, are granted by Apple herein, including but not limited to any
// patent rights that may be infringed by your derivative works or by other
// works in which the Apple Software may be incorporated.
//
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (C) 2009 Apple Inc. All Rights Reserved.
//
//*****************************************************
#include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
#ifdef DEBUG // not used in release
#if !defined (kBuildingLibrary)
#define kVerboseErrors
// system includes ----------------------------------------------------------
#ifdef kVerboseErrors
//#include <Carbon/Carbon.h>
#endif
#endif // not kBuildingLibrary
#endif // DEBUG
#include <stdio.h>
// project includes ---------------------------------------------------------
#include "HID_Utilities_External.h"
// globals (internal/private) -----------------------------------------------
// prototypes (internal/private) --------------------------------------------
// functions (internal/private) ---------------------------------------------
#pragma mark -
// -------------------------------------
// central error reporting
void HIDReportErrorNum(const char *strError, int numError) {
char errMsgCStr[256];
sprintf(errMsgCStr, "%s #%d (0x%x)", strError, numError, numError);
// out as debug string
#ifdef kVerboseErrors
{
fputs(errMsgCStr, stderr);
}
#endif // kVerboseErrors
} // HIDReportErrorNum
// -------------------------------------
void HIDReportError(const char *strError) {
char errMsgCStr[256];
sprintf(errMsgCStr, "%s", strError);
// out as debug string
#ifdef kVerboseErrors
{
fputs(errMsgCStr, stderr);
}
#endif // kVerboseErrors
} // HIDReportError
#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050

1210
src/cocoa/HID_Name_Lookup.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,361 @@
// File: HID_Queue_Utilities.c
// Abstract: HID Queue Utilities.
// Version: 2.0
//
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
// Inc. ("Apple") in consideration of your agreement to the following
// terms, and your use, installation, modification or redistribution of
// this Apple software constitutes acceptance of these terms. If you do
// not agree with these terms, please do not use, install, modify or
// redistribute this Apple software.
//
// In consideration of your agreement to abide by the following terms, and
// subject to these terms, Apple grants you a personal, non-exclusive
// license, under Apple's copyrights in this original Apple software (the
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
// Software, with or without modifications, in source and/or binary forms;
// provided that if you redistribute the Apple Software in its entirety and
// without modifications, you must retain this notice and the following
// text and disclaimers in all such redistributions of the Apple Software.
// Neither the name, trademarks, service marks or logos of Apple Inc. may
// be used to endorse or promote products derived from the Apple Software
// without specific prior written permission from Apple. Except as
// expressly stated in this notice, no other rights or licenses, express or
// implied, are granted by Apple herein, including but not limited to any
// patent rights that may be infringed by your derivative works or by other
// works in which the Apple Software may be incorporated.
//
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (C) 2009 Apple Inc. All Rights Reserved.
//
//*****************************************************
#include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
#include "HID_Utilities_External.h"
// ==================================
// private functions
// creates a queue for a device, creates and opens device interface if required
static IOReturn HIDCreateQueue(IOHIDDeviceRef inIOHIDDeviceRef) {
IOReturn result = kIOReturnSuccess;
if ( inIOHIDDeviceRef ) {
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) );
// do we already have a queue?
IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef);
if ( tIOHIDQueueRef ) { // (yes)
assert( IOHIDQueueGetTypeID() == CFGetTypeID(tIOHIDQueueRef) );
} else {
tIOHIDQueueRef = IOHIDQueueCreate(kCFAllocatorDefault, inIOHIDDeviceRef, kDeviceQueueSize, kIOHIDOptionsTypeNone);
if ( tIOHIDQueueRef ) { // did that work
IOHIDDevice_SetQueue(inIOHIDDeviceRef, tIOHIDQueueRef);
result = kIOReturnSuccess;
} else {
HIDReportErrorNum("Failed to create queue via create", result);
}
}
} else {
HIDReportErrorNum("HID device ref does not exist for queue creation", result);
}
return (result);
} /* HIDCreateQueue */
// ---------------------------------
// returns true if queue is empty false otherwise
// error if no device, empty if no queue
static unsigned char HIDIsDeviceQueueEmpty(IOHIDDeviceRef inIOHIDDeviceRef) {
if ( inIOHIDDeviceRef ) { // need device and queue
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) );
IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef);
if ( tIOHIDQueueRef ) {
IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO);
while ( tIOHIDElementRef ) {
if ( IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) {
return (false);
}
tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO);
}
} else {
HIDReportError("NULL device passed to HIDIsDeviceQueueEmpty.");
}
} else {
HIDReportError("NULL device passed to HIDIsDeviceQueueEmpty.");
}
return (true);
} /* HIDIsDeviceQueueEmpty */
// ---------------------------------
// disposes and releases queue, sets queue to NULL,.
// Note: will have no effect if device or queue do not exist
static IOReturn HIDDisposeReleaseQueue(IOHIDDeviceRef inIOHIDDeviceRef) {
IOReturn result = kIOReturnSuccess;
if ( inIOHIDDeviceRef ) {
IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef);
if ( tIOHIDQueueRef ) {
// stop queue
IOHIDQueueStop(tIOHIDQueueRef);
// release the queue
CFRelease(tIOHIDQueueRef);
}
} else {
HIDReportError("NULL device passed to HIDDisposeReleaseQueue.");
}
return (result);
} /* HIDDisposeReleaseQueue */
// ==================================
// public functions
// ----------------------------------
// queues specific element, performing any device queue set up required
// queue is started and ready to return events on exit from this function
int HIDQueueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) {
IOReturn result = kIOReturnSuccess;
if ( inIOHIDDeviceRef ) {
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) );
if ( inIOHIDElementRef ) {
assert( IOHIDElementGetTypeID() == CFGetTypeID(inIOHIDElementRef) );
IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef);
if ( !tIOHIDQueueRef ) { // if no queue create queue
result = HIDCreateQueue(inIOHIDDeviceRef);
if ( kIOReturnSuccess == result ) {
tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef);
}
}
if ( tIOHIDQueueRef ) {
// stop queue
IOHIDQueueStop(tIOHIDQueueRef);
// queue element
if ( !IOHIDQueueContainsElement(tIOHIDQueueRef, inIOHIDElementRef) ) {
IOHIDQueueAddElement(tIOHIDQueueRef, inIOHIDElementRef);
}
// restart queue
IOHIDQueueStart(tIOHIDQueueRef);
} else {
HIDReportError("No queue for device passed to HIDQueueElement.");
if ( kIOReturnSuccess == result ) {
result = kIOReturnError;
}
}
} else {
HIDReportError("NULL element passed to HIDQueueElement.");
result = kIOReturnBadArgument;
}
} else {
HIDReportError("NULL device passed to HIDQueueElement.");
result = kIOReturnBadArgument;
}
return (result);
} /* HIDQueueElement */
// ---------------------------------
// adds all elements to queue, performing any device queue set up required
// queue is started and ready to return events on exit from this function
int HIDQueueDevice(IOHIDDeviceRef inIOHIDDeviceRef) {
IOReturn result = kIOReturnSuccess;
// error checking
if ( !inIOHIDDeviceRef ) {
HIDReportError("Device does not exist, cannot queue device.");
return (kIOReturnBadArgument);
}
if ( !inIOHIDDeviceRef ) { // must have interface
HIDReportError("Device does not have hid device ref, cannot queue device.");
return (kIOReturnError);
}
IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef);
if ( !tIOHIDQueueRef ) { // if no queue create queue
result = HIDCreateQueue(inIOHIDDeviceRef);
if ( kIOReturnSuccess == result ) {
tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef);
}
}
if ( (kIOReturnSuccess != result) || (!tIOHIDQueueRef) ) {
HIDReportErrorNum("Could not queue device due to problem creating queue.", result);
if ( kIOReturnSuccess != result ) {
return (result);
} else {
return (kIOReturnError);
}
}
// stop queue
IOHIDQueueStop(tIOHIDQueueRef);
// queue element
IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO);
while ( tIOHIDElementRef ) {
if ( !IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) {
IOHIDQueueAddElement(tIOHIDQueueRef, tIOHIDElementRef);
}
tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO);
}
// restart queue
IOHIDQueueStart(tIOHIDQueueRef);
return (result);
} /* HIDQueueDevice */
// ---------------------------------
// removes element for queue, if last element in queue will release queue and closes device interface
int HIDDequeueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHIDElementRef) {
IOReturn result = kIOReturnSuccess;
if ( inIOHIDDeviceRef ) {
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) );
if ( inIOHIDElementRef ) {
assert( IOHIDElementGetTypeID() == CFGetTypeID(inIOHIDElementRef) );
IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef);
if ( tIOHIDQueueRef ) {
// stop queue
IOHIDQueueStop(tIOHIDQueueRef);
// de-queue element
if ( IOHIDQueueContainsElement(tIOHIDQueueRef, inIOHIDElementRef) ) {
IOHIDQueueRemoveElement(tIOHIDQueueRef, inIOHIDElementRef);
}
// release device queue and close interface if queue empty
if ( HIDIsDeviceQueueEmpty(inIOHIDDeviceRef) ) {
result = HIDDisposeReleaseQueue(inIOHIDDeviceRef);
if ( kIOReturnSuccess != result ) {
HIDReportErrorNum("Failed to dispose and release queue.", result);
}
} else { // not empty so restart queue
IOHIDQueueStart(tIOHIDQueueRef);
}
} else {
HIDReportError("No queue for device passed to HIDDequeueElement.");
if ( kIOReturnSuccess == result ) {
result = kIOReturnError;
}
}
} else {
HIDReportError("NULL element passed to HIDDequeueElement.");
result = kIOReturnBadArgument;
}
} else {
HIDReportError("NULL device passed to HIDDequeueElement.");
result = kIOReturnBadArgument;
}
return (result);
} /* HIDDequeueElement */
// ---------------------------------
// completely removes all elements from queue and releases queue and closes device interface
// does not release device interfaces, application must call ReleaseHIDDeviceList on exit
int HIDDequeueDevice(IOHIDDeviceRef inIOHIDDeviceRef) {
IOReturn result = kIOReturnSuccess;
// error checking
if ( !inIOHIDDeviceRef ) {
HIDReportError("Device does not exist, cannot queue device.");
return (kIOReturnBadArgument);
}
if ( !inIOHIDDeviceRef ) { // must have interface
HIDReportError("Device does not have hid device ref, cannot queue device.");
return (kIOReturnError);
}
IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef);
if ( tIOHIDQueueRef ) {
// iterate through elements and if queued, remove
IOHIDElementRef tIOHIDElementRef = HIDGetFirstDeviceElement(inIOHIDDeviceRef, kHIDElementTypeIO);
while ( tIOHIDElementRef ) {
// de-queue element
if ( IOHIDQueueContainsElement(tIOHIDQueueRef, tIOHIDElementRef) ) {
IOHIDQueueRemoveElement(tIOHIDQueueRef, tIOHIDElementRef);
}
tIOHIDElementRef = HIDGetNextDeviceElement(tIOHIDElementRef, kHIDElementTypeIO);
}
// ensure queue is disposed and released
result = HIDDisposeReleaseQueue(inIOHIDDeviceRef);
if ( kIOReturnSuccess != result ) {
HIDReportErrorNum("Failed to dispose and release queue.", result);
}
} else {
HIDReportError("No queue for device passed to HIDDequeueElement.");
if ( kIOReturnSuccess == result ) {
result = kIOReturnError;
}
}
return (result);
} /* HIDDequeueDevice */
// ---------------------------------
// releases all device queues for quit or rebuild (must be called)
// does not release device interfaces, application must call ReleaseHIDDeviceList on exit
IOReturn HIDReleaseAllDeviceQueues(void) {
IOReturn result = kIOReturnSuccess;
IOHIDDeviceRef tIOHIDDeviceRef = HIDGetFirstDevice();
while ( tIOHIDDeviceRef ) {
result = HIDDequeueDevice(tIOHIDDeviceRef);
if ( kIOReturnSuccess != result ) {
HIDReportErrorNum("Could not dequeue device.", result);
}
tIOHIDDeviceRef = HIDGetNextDevice(tIOHIDDeviceRef);
}
return (result);
} /* HIDReleaseAllDeviceQueues */
// ---------------------------------
// Get the next event in the queue for a device
// elements or entire device should be queued prior to calling this with HIDQueueElement or HIDQueueDevice
// returns true if an event is avialable for the element and fills out *pHIDEvent structure, returns false otherwise
// Note: kIOReturnUnderrun returned from getNextEvent indicates an empty queue not an error condition
// Note: application should pass in a pointer to a IOHIDEventStruct cast to a void (for CFM compatibility)
unsigned char HIDGetEvent(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDValueRef *pIOHIDValueRef) {
if ( inIOHIDDeviceRef ) {
IOHIDQueueRef tIOHIDQueueRef = IOHIDDevice_GetQueue(inIOHIDDeviceRef);
if ( tIOHIDQueueRef ) {
if ( pIOHIDValueRef ) {
*pIOHIDValueRef = IOHIDQueueCopyNextValueWithTimeout(tIOHIDQueueRef, 0.0);
if ( *pIOHIDValueRef ) {
return (true);
}
}
} else {
HIDReportError("Could not get HID event, hid queue reference does not exist.");
}
} else {
HIDReportError("Could not get HID event, device does not exist.");
}
return (false); // did not get event
} /* HIDGetEvent */
#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050

1068
src/cocoa/HID_Utilities.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,417 @@
// File: HID_Utilities_External.h
// Abstract: External interface for HID Utilities, can be used with either library or source.
// Version: 2.0
//
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
// Inc. ("Apple") in consideration of your agreement to the following
// terms, and your use, installation, modification or redistribution of
// this Apple software constitutes acceptance of these terms. If you do
// not agree with these terms, please do not use, install, modify or
// redistribute this Apple software.
//
// In consideration of your agreement to abide by the following terms, and
// subject to these terms, Apple grants you a personal, non-exclusive
// license, under Apple's copyrights in this original Apple software (the
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
// Software, with or without modifications, in source and/or binary forms;
// provided that if you redistribute the Apple Software in its entirety and
// without modifications, you must retain this notice and the following
// text and disclaimers in all such redistributions of the Apple Software.
// Neither the name, trademarks, service marks or logos of Apple Inc. may
// be used to endorse or promote products derived from the Apple Software
// without specific prior written permission from Apple. Except as
// expressly stated in this notice, no other rights or licenses, express or
// implied, are granted by Apple herein, including but not limited to any
// patent rights that may be infringed by your derivative works or by other
// works in which the Apple Software may be incorporated.
//
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (C) 2009 Apple Inc. All Rights Reserved.
//
//*****************************************************
#ifndef _HID_Utilities_External_h_
#define _HID_Utilities_External_h_
// ==================================
#ifdef __cplusplus
extern "C" {
#endif
// ==================================
//includes
#include <stdio.h>
#include "IOHIDLib_.h"
// ==================================
#ifndef _IOKIT_HID_IOHIDKEYS_H_
/*!
@typedef IOHIDElementCookie
@abstract Abstract data type used as a unique identifier for an element.
*/
#ifdef __LP64__
typedef uint32_t IOHIDElementCookie;
#else
typedef void *IOHIDElementCookie;
#endif
#endif
// Device and Element Interfaces
enum HIDElementTypeMask {
kHIDElementTypeInput = 1 << 1,
kHIDElementTypeOutput = 1 << 2,
kHIDElementTypeFeature = 1 << 3,
kHIDElementTypeCollection = 1 << 4,
kHIDElementTypeIO = kHIDElementTypeInput | kHIDElementTypeOutput | kHIDElementTypeFeature,
kHIDElementTypeAll = kHIDElementTypeIO | kHIDElementTypeCollection
};
typedef enum HIDElementTypeMask HIDElementTypeMask;
// ==================================
//*****************************************************
#pragma mark - exported globals
//-----------------------------------------------------
extern IOHIDManagerRef gIOHIDManagerRef;
extern CFMutableArrayRef gDeviceCFArrayRef;
extern CFArrayRef gElementCFArrayRef;
//*************************************************************************
//
// HIDBuildMultiDeviceList( inUsagePages, inUsages, inNumDeviceTypes )
//
// Purpose: builds list of devices with elements (allocates memory and captures devices) in which
// the devices could be of different types/usages list is allocated internally within HID
// Utilites and can be accessed via accessor functions structures within list are considered
// flat and user accessable, but not user modifiable can be called again to rebuild list to
// account for new devices (will do the right thing in case of disposing existing list)
// usagePage, usage are each a numDeviceTypes sized array of matching usage and usage pages
// returns true if succesful
//
// Inputs: inUsagePages - inNumDeviceTypes sized array of matching usage pages
// inUsages - inNumDeviceTypes sized array of matching usages
// inNumDeviceTypes - number of usage pages & usages
//
// Returns: Boolean - if successful
//
extern Boolean HIDBuildMultiDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes);
// same as above but this uses a single usagePage and usage
extern Boolean HIDBuildDeviceList(UInt32 usagePage, UInt32 usage);
// updates the current device list for any new/removed devices
// if this is called before HIDBuildDeviceList the it functions like HIDBuildMultiDeviceList
// usagePage, usage are each a numDeviceTypes sized array of matching usage and usage pages
// returns true if successful which means if any device were added or removed (the device config changed)
extern Boolean HIDUpdateDeviceList(const UInt32 *inUsagePages, const UInt32 *inUsages, int inNumDeviceTypes);
// release list built by above function
// MUST be called prior to application exit to properly release devices
// if not called (or app crashes) devices can be recovered by pluging into different location in USB chain
extern void HIDReleaseDeviceList(void);
//*************************************************************************
//
// HIDRebuildDevices( )
//
// Purpose: rebuilds the (internal) list of devices
//
// Inputs: none
//
// Returns: none
//
extern void HIDRebuildDevices(void);
// does a device list exist
extern unsigned char HIDHaveDeviceList(void);
// how many HID devices have been found
// returns 0 if no device list exist
extern UInt32 HIDCountDevices(void);
// how many elements does a specific device have
// returns 0 if device is invalid or NULL
// uses mask of HIDElementTypeMask to restrict element found
// use kHIDElementTypeIO to get non-collection elements
extern UInt32 HIDCountDeviceElements(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask);
// get the first device in the device list
// returns NULL if no list exists
extern IOHIDDeviceRef HIDGetFirstDevice(void);
// get next device in list given current device as parameter
// returns NULL if end of list
extern IOHIDDeviceRef HIDGetNextDevice(IOHIDDeviceRef inIOHIDDeviceRef);
// get the first element of device passed in as parameter
// returns NULL if no list exists or device does not exists or is NULL
// uses mask of HIDElementTypeMask to restrict element found
// use kHIDElementTypeIO to get previous HIDGetFirstDeviceElement functionality
extern IOHIDElementRef HIDGetFirstDeviceElement(IOHIDDeviceRef inIOHIDDeviceRef, HIDElementTypeMask typeMask);
// get next element of given device in list given current element as parameter
// will walk down each collection then to next element or collection (depthwise traverse)
// returns NULL if end of list
// uses mask of HIDElementTypeMask to restrict element found
// use kHIDElementTypeIO to get previous HIDGetNextDeviceElement functionality
extern IOHIDElementRef HIDGetNextDeviceElement(IOHIDElementRef inIOHidElementRef, HIDElementTypeMask typeMask);
// get previous element of given device in list given current element as parameter
// this walks directly up the tree to the top element and does not search at each level
// returns NULL if beginning of list
// uses mask of HIDElementTypeMask to restrict element found
// use kHIDElementTypeIO to get non-collection elements
extern IOHIDElementRef HIDGetPreviousDeviceElement(IOHIDElementRef inIOHidElementRef, HIDElementTypeMask typeMask);
// returns C string type name given a type enumeration passed in as parameter( see IOHIDKeys.h )
// returns empty string for invalid types
extern void HIDGetTypeName(IOHIDElementType inIOHIDElementType, char *outCStrName);
//*************************************************************************
//
// HIDCopyUsageName( inUsagePage, inUsage )
//
// Purpose: return a CFStringRef string for a given usage page & usage( see IOUSBHIDParser.h )
//
// Notes: returns usage page and usage values in CFString form for unknown values
//
// Inputs: inUsagePage - the usage page
// inUsage - the usage
//
// Returns: CFStringRef - the resultant string
//
extern CFStringRef HIDCopyUsageName(long inUsagePage, long inUsage);
// ==================================
// Element Event Queue and Value Interfaces
enum {
kDefaultUserMin = 0, // default user min and max used for scaling
kDefaultUserMax = 255
};
enum {
kDeviceQueueSize = 50 // this is wired kernel memory so should be set to as small as possible
// but should account for the maximum possible events in the queue
// USB updates will likely occur at 100 Hz so one must account for this rate of
// if states change quickly (updates are only posted on state changes)
};
// ==================================
// queues specific element, performing any device queue set up required
extern int HIDQueueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHidElementRef);
// adds all elements to queue, performing any device queue set up required
extern int HIDQueueDevice(IOHIDDeviceRef inIOHIDDeviceRef);
// removes element for queue, if last element in queue will release queue and device
extern int HIDDequeueElement(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHidElementRef);
// completely removes all elements from queue and releases queue and device
extern int HIDDequeueDevice(IOHIDDeviceRef inIOHIDDeviceRef);
// releases all device queues for quit or rebuild (must be called)
extern int HIDReleaseAllDeviceQueues(void);
// returns true if an event is avialable for the element and fills out *pHIDEvent structure, returns false otherwise
// pHIDEvent is a poiner to a IOHIDEventStruct, using void here for compatibility, users can cast a required
extern unsigned char HIDGetEvent(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDValueRef *pIOHIDValueRef);
// ==================================
// Conguration and Save Interfaces
enum {
kPercentMove = 10 // precent of overall range a element must move to register
};
typedef struct HID_info_struct {
int actionCookie;
// device
// need to add serial number when I have a test case
struct {
int vendorID, productID;
int locID;
uint32_t usagePage, usage;
} device;
// elements
struct {
uint32_t usagePage, usage;
int minReport, maxReport;
IOHIDElementCookie cookie; // always 32 bits
} element;
}HID_info_rec, *HID_info_ptr;
// get vendor name from vendor ID
extern Boolean HIDGetVendorNameFromVendorID(long inVendorID, char *outCStrName);
// get product name from vendor/product ID
extern Boolean HIDGetProductNameFromVendorProductID(long inVendorID, long inProductID, char *outCStrName);
// get element name from vendor id/product id look up ( using element cookie )
extern Boolean HIDGetElementNameFromVendorProductCookie(int inVendorID,
int inProductID,
IOHIDElementCookie inCookie,
char * outCStrName);
// get element name from vendor id/product id look up ( using element usage page & usage )
extern Boolean HIDGetElementNameFromVendorProductUsage(long inVendorID,
long inProductID,
long inUsagePage,
long inUsage,
char *inCStrName);
// utility routines to dump device or element info
extern void HIDDumpDeviceInfo(IOHIDDeviceRef inIOHIDDeviceRef);
extern void HIDDumpElementInfo(IOHIDElementRef inIOHIDElementRef);
extern void HIDDumpElementCalibrationInfo(IOHIDElementRef inIOHIDElementRef);
// polls single device's elements for a change greater than kPercentMove. Times out after given time
// returns 1 and pointer to element if found
// returns 0 and NULL for both parameters if not found
extern unsigned char HIDConfigureSingleDeviceAction(IOHIDDeviceRef inIOHIDDeviceRef,
IOHIDElementRef *outIOHIDElementRef,
float timeout);
//*************************************************************************
//
// HIDConfigureAction( outDeviceRef, outElementRef, inTimeout )
//
// Purpose: polls all devices and elements for a change greater than kPercentMove.
// Times out after given time returns 1 and pointer to device and element
// if found; returns 0 and NULL for both parameters if not found
//
// Inputs: outDeviceRef - address where to store the device
// outElementRef - address where to store the element
// inTimeout - the timeout
// Returns: Boolean - TRUE if successful
// outDeviceRef - the device
// outElementRef - the element
//
extern Boolean HIDConfigureAction(IOHIDDeviceRef *outDeviceRef, IOHIDElementRef *outElementRef, float inTimeout);
//*************************************************************************
//
// HIDSaveElementPref( inKeyCFStringRef, inAppCFStringRef, inDeviceRef, inElementRef )
//
// Purpose: Save the device & element values into the specified key in the specified applications preferences
//
// Inputs: inKeyCFStringRef - the preference key
// inAppCFStringRef - the application identifier
// inDeviceRef - the device
// inElementRef - the element
// Returns: Boolean - if successful
//
extern Boolean HIDSaveElementPref(const CFStringRef inKeyCFStringRef,
CFStringRef inAppCFStringRef,
IOHIDDeviceRef inDeviceRef,
IOHIDElementRef inElementRef);
//*************************************************************************
//
// HIDRestoreElementPref( inKeyCFStringRef, inAppCFStringRef, outDeviceRef, outElementRef )
//
// Purpose: Find the specified preference in the specified application
//
// Inputs: inKeyCFStringRef - the preference key
// inAppCFStringRef - the application identifier
// outDeviceRef - address where to restore the device
// outElementRef - address where to restore the element
// Returns: Boolean - if successful
// outDeviceRef - the device
// outElementRef - the element
//
extern Boolean HIDRestoreElementPref(CFStringRef inKeyCFStringRef,
CFStringRef inAppCFStringRef,
IOHIDDeviceRef * outDeviceRef,
IOHIDElementRef *outElementRef);
//*************************************************************************
//
// HIDFindDeviceAndElement( inSearchInfo, outFoundDevice, outFoundElement )
//
// Purpose: find the closest matching device and element for this action
//
// Notes: matches device: serial, vendorID, productID, location, inUsagePage, usage
// matches element: cookie, inUsagePage, usage,
//
// Inputs: inSearchInfo - the device & element info we searching for
// outFoundDevice - the address of the best matching device
// outFoundElement - the address of the best matching element
//
// Returns: Boolean - TRUE if we find a match
// outFoundDevice - the best matching device
// outFoundElement - the best matching element
//
extern Boolean HIDFindDeviceAndElement(const HID_info_rec *inSearchInfo,
IOHIDDeviceRef * outFoundDevice,
IOHIDElementRef * outFoundElement);
// -- These are routines to use if the applcationwants HID Utilities to do the file handling --
// Note: the FILE * is a MachO posix FILE and will not likely work directly with MW MSL FILE * type.
// take input records, save required info
// assume file is open and at correct position.
void HIDSaveElementConfig(FILE *fileRef, IOHIDDeviceRef inIOHIDDeviceRef, IOHIDElementRef inIOHidElementRef, int actionCookie);
// takes a file, reads one record (assume file position is correct and file is open)
// search for matching device
// return tIOHIDDeviceRef, tIOHIDElementRef and cookie for action
int HIDRestoreElementConfig(FILE *fileRef, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef);
// -- These are routines to use if the client wants to use their own file handling --
// Set up a config record for saving
// takes an input records, returns record user can save as they want
// Note: the save rec must be pre-allocated by the calling app and will be filled out
void HIDSetElementConfig(HID_info_ptr inHIDInfoPtr,
IOHIDDeviceRef inIOHIDDeviceRef,
IOHIDElementRef inIOHidElementRef,
int actionCookie);
// Get matching element from config record
// takes a pre-allocated and filled out config record
// search for matching device
// return tIOHIDDeviceRef, tIOHIDElementRef and cookie for action
int HIDGetElementConfig(HID_info_ptr inHIDInfoPtr, IOHIDDeviceRef *outIOHIDDeviceRef, IOHIDElementRef *outIOHIDElementRef);
// ==================================
// Error reporter, can be set to report however the application desires
extern void HIDReportError(const char *strError);
// Error with numeric code reporter, can be set to report however the application desires
extern void HIDReportErrorNum(const char *strError, int numError);
#ifdef __cplusplus
}
#endif
#endif // _HID_Utilities_External_h_

619
src/cocoa/IOHIDDevice_.c Normal file
View File

@ -0,0 +1,619 @@
// File: IOHIDDevice_.c
// Abstract: convieance functions for IOHIDDeviceGetProperty
// Version: 2.0 + 5.3
//
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
// Inc. ("Apple") in consideration of your agreement to the following
// terms, and your use, installation, modification or redistribution of
// this Apple software constitutes acceptance of these terms. If you do
// not agree with these terms, please do not use, install, modify or
// redistribute this Apple software.
//
// In consideration of your agreement to abide by the following terms, and
// subject to these terms, Apple grants you a personal, non-exclusive
// license, under Apple's copyrights in this original Apple software (the
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
// Software, with or without modifications, in source and/or binary forms;
// provided that if you redistribute the Apple Software in its entirety and
// without modifications, you must retain this notice and the following
// text and disclaimers in all such redistributions of the Apple Software.
// Neither the name, trademarks, service marks or logos of Apple Inc. may
// be used to endorse or promote products derived from the Apple Software
// without specific prior written permission from Apple. Except as
// expressly stated in this notice, no other rights or licenses, express or
// implied, are granted by Apple herein, including but not limited to any
// patent rights that may be infringed by your derivative works or by other
// works in which the Apple Software may be incorporated.
//
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (C) 2009 Apple Inc. All Rights Reserved.
//
//*****************************************************
#include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
#pragma mark - includes & imports
//-----------------------------------------------------
#include "IOHIDDevice_.h"
//*****************************************************
#pragma mark - typedef's, struct's, enums, defines, etc.
//-----------------------------------------------------
#define kIOHIDDevice_TransactionKey "DeviceTransactionRef"
#define kIOHIDDevice_QueueKey "DeviceQueueRef"
//*****************************************************
#pragma mark - local (static) function prototypes
//-----------------------------------------------------
static Boolean IOHIDDevice_GetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef,
CFStringRef inKey,
uint32_t * outValue);
// static void IOHIDDevice_SetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, uint32_t inValue);
static Boolean IOHIDDevice_GetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef,
CFStringRef inKey,
void ** outValue);
static void IOHIDDevice_SetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef,
CFStringRef inKey,
void * inValue);
//*****************************************************
#pragma mark - exported globals
//-----------------------------------------------------
//*****************************************************
#pragma mark - local (static) globals
//-----------------------------------------------------
//*****************************************************
#pragma mark - exported function implementations
//-----------------------------------------------------
//*************************************************************************
//
// HIDIsValidDevice( inIOHIDDeviceRef )
//
// Purpose: validate this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: Boolean - TRUE if we find the device in our( internal ) device list
//
Boolean HIDIsValidDevice(IOHIDDeviceRef inIOHIDDeviceRef) {
Boolean result = FALSE; // assume failure (pessimist!)
if ( inIOHIDDeviceRef ) {
if ( CFGetTypeID(inIOHIDDeviceRef) ==IOHIDDeviceGetTypeID() ) {
result = TRUE;
}
}
return (result);
} // HIDIsValidDevice
//*************************************************************************
//
// IOHIDDevice_GetTransport( inIOHIDDeviceRef )
//
// Purpose: get the Transport CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFStringRef - the Transport for this device
//
CFStringRef IOHIDDevice_GetTransport(IOHIDDeviceRef inIOHIDDeviceRef) {
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) );
return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDTransportKey) ) );
}
//*************************************************************************
//
// IOHIDDevice_GetVendorID( inIOHIDDeviceRef )
//
// Purpose: get the vendor ID for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the vendor ID for this device
//
uint32_t IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDKey), &result);
return (result);
} // IOHIDDevice_GetVendorID
//*************************************************************************
//
// IOHIDDevice_GetVendorIDSource( inIOHIDDeviceRef )
//
// Purpose: get the VendorIDSource for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the VendorIDSource for this device
//
uint32_t IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVendorIDSourceKey), &result);
return (result);
} // IOHIDDevice_GetVendorIDSource
//*************************************************************************
//
// IOHIDDevice_GetProductID( inIOHIDDeviceRef )
//
// Purpose: get the product ID for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the product ID for this device
//
uint32_t IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDProductIDKey), &result);
return (result);
} // IOHIDDevice_GetProductID
//*************************************************************************
//
// IOHIDDevice_GetVersionNumber( inIOHIDDeviceRef )
//
// Purpose: get the VersionNumber CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the VersionNumber for this device
//
uint32_t IOHIDDevice_GetVersionNumber(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDVersionNumberKey), &result);
return (result);
} // IOHIDDevice_GetVersionNumber
//*************************************************************************
//
// IOHIDDevice_GetManufacturer( inIOHIDDeviceRef )
//
// Purpose: get the Manufacturer CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFStringRef - the Manufacturer for this device
//
CFStringRef IOHIDDevice_GetManufacturer(IOHIDDeviceRef inIOHIDDeviceRef) {
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) );
return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDManufacturerKey) ) );
} // IOHIDDevice_GetManufacturer
//*************************************************************************
//
// IOHIDDevice_GetProduct( inIOHIDDeviceRef )
//
// Purpose: get the Product CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFStringRef - the Product for this device
//
CFStringRef IOHIDDevice_GetProduct(IOHIDDeviceRef inIOHIDDeviceRef) {
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) );
return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDProductKey) ) );
} // IOHIDDevice_GetProduct
//*************************************************************************
//
// IOHIDDevice_GetSerialNumber( inIOHIDDeviceRef )
//
// Purpose: get the SerialNumber CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFStringRef - the SerialNumber for this device
//
CFStringRef IOHIDDevice_GetSerialNumber(IOHIDDeviceRef inIOHIDDeviceRef) {
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) );
return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDSerialNumberKey) ) );
}
//*************************************************************************
//
// IOHIDDevice_GetCountryCode( inIOHIDDeviceRef )
//
// Purpose: get the CountryCode CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the CountryCode for this device
//
uint32_t IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDCountryCodeKey), &result);
return (result);
} // IOHIDDevice_GetCountryCode
//*************************************************************************
//
// IOHIDDevice_GetLocationID( inIOHIDDeviceRef )
//
// Purpose: get the location ID for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the location ID for this device
//
uint32_t IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey), &result);
return (result);
} // IOHIDDevice_GetLocationID
//*************************************************************************
//
// IOHIDDevice_GetUsage( inIOHIDDeviceRef )
//
// Purpose: get the usage for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the usage for this device
//
uint32_t IOHIDDevice_GetUsage(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsageKey), &result);
return (result);
} // IOHIDDevice_GetUsage
//*************************************************************************
//
// IOHIDDevice_GetUsagePage( inIOHIDDeviceRef )
//
// Purpose: get the usage page for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the usage page for this device
//
uint32_t IOHIDDevice_GetUsagePage(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsagePageKey), &result);
return (result);
} // IOHIDDevice_GetUsagePage
//*************************************************************************
//
// IOHIDDevice_GetUsagePairs( inIOHIDDeviceRef )
//
// Purpose: get the UsagePairs CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFArrayRef - the UsagePairs for this device
//
CFArrayRef IOHIDDevice_GetUsagePairs(IOHIDDeviceRef inIOHIDDeviceRef) {
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) );
return ( IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDDeviceUsagePairsKey) ) );
}
//*************************************************************************
//
// IOHIDDevice_GetPrimaryUsage( inIOHIDDeviceRef )
//
// Purpose: get the PrimaryUsage CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the PrimaryUsage for this device
//
uint32_t IOHIDDevice_GetPrimaryUsage(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsageKey), &result);
return (result);
} // IOHIDDevice_GetPrimaryUsage
//*************************************************************************
//
// IOHIDDevice_GetPrimaryUsagePage( inIOHIDDeviceRef )
//
// Purpose: get the PrimaryUsagePage CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the PrimaryUsagePage for this device
//
uint32_t IOHIDDevice_GetPrimaryUsagePage(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDPrimaryUsagePageKey), &result);
return (result);
} // IOHIDDevice_GetPrimaryUsagePage
//*************************************************************************
//
// IOHIDDevice_GetMaxInputReportSize( inIOHIDDeviceRef )
//
// Purpose: get the MaxInputReportSize CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the MaxInputReportSize for this device
//
uint32_t IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxInputReportSizeKey), &result);
return (result);
} // IOHIDDevice_GetMaxInputReportSize
//*************************************************************************
//
// IOHIDDevice_GetMaxOutputReportSize( inIOHIDDeviceRef )
//
// Purpose: get the MaxOutputReportSize for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the MaxOutput for this device
//
uint32_t IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxOutputReportSizeKey), &result);
return (result);
} // IOHIDDevice_GetMaxOutputReportSize
//*************************************************************************
//
// IOHIDDevice_GetMaxFeatureReportSize( inIOHIDDeviceRef )
//
// Purpose: get the MaxFeatureReportSize for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the MaxFeatureReportSize for this device
//
uint32_t IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDMaxFeatureReportSizeKey), &result);
return (result);
} // IOHIDDevice_GetMaxFeatureReportSize
//*************************************************************************
//
// IOHIDDevice_GetReportInterval( inIOHIDDeviceRef )
//
// Purpose: get the ReportInterval for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the ReportInterval for this device
//
#ifndef kIOHIDReportIntervalKey
#define kIOHIDReportIntervalKey "ReportInterval"
#endif
uint32_t IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef) {
uint32_t result = 0;
(void) IOHIDDevice_GetUInt32Property(inIOHIDDeviceRef, CFSTR(kIOHIDReportIntervalKey), &result);
return (result);
} // IOHIDDevice_GetReportInterval
//*************************************************************************
//
// IOHIDDevice_GetQueue( inIOHIDDeviceRef )
//
// Purpose: get the Queue for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: IOHIDQueueRef - the Queue for this device
//
IOHIDQueueRef IOHIDDevice_GetQueue(IOHIDDeviceRef inIOHIDDeviceRef) {
IOHIDQueueRef result = 0;
(void) IOHIDDevice_GetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), (void *) &result);
if ( result ) {
assert( IOHIDQueueGetTypeID() == CFGetTypeID(result) );
}
return (result);
} // IOHIDDevice_GetQueue
//*************************************************************************
//
// IOHIDDevice_SetQueue( inIOHIDDeviceRef, inQueueRef )
//
// Purpose: Set the Queue for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
// inQueueRef - the Queue reference
//
// Returns: nothing
//
void IOHIDDevice_SetQueue(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDQueueRef inQueueRef) {
IOHIDDevice_SetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_QueueKey), inQueueRef);
}
//*************************************************************************
//
// IOHIDDevice_GetTransaction( inIOHIDDeviceRef )
//
// Purpose: get the Transaction for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: IOHIDTransactionRef - the Transaction for this device
//
IOHIDTransactionRef IOHIDDevice_GetTransaction(IOHIDDeviceRef inIOHIDDeviceRef) {
IOHIDTransactionRef result = 0;
(void) IOHIDDevice_GetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), (void *) &result);
return (result);
} // IOHIDDevice_GetTransaction
//*************************************************************************
//
// IOHIDDevice_SetTransaction( inIOHIDDeviceRef, inTransactionRef )
//
// Purpose: Set the Transaction for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
// inTransactionRef - the Transaction reference
//
// Returns: nothing
//
void IOHIDDevice_SetTransaction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDTransactionRef inTransactionRef) {
IOHIDDevice_SetPtrProperty(inIOHIDDeviceRef, CFSTR(kIOHIDDevice_TransactionKey), inTransactionRef);
}
//*****************************************************
#pragma mark - local (static) function implementations
//-----------------------------------------------------
//*************************************************************************
//
// IOHIDDevice_GetUInt32Property( inIOHIDDeviceRef, inKey, outValue )
//
// Purpose: convieance function to return a uint32_t property of a device
//
// Inputs: inIOHIDDeviceRef - the device
// inKey - CFString for the
// outValue - address where to restore the element
// Returns: the action cookie
// outValue - the device
//
static Boolean IOHIDDevice_GetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, uint32_t *outValue) {
Boolean result = FALSE;
if ( inIOHIDDeviceRef ) {
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) );
CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey);
if ( tCFTypeRef ) {
// if this is a number
if ( CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef) ) {
// get it's value
result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, outValue );
}
}
}
return (result);
} // IOHIDDevice_GetUInt32Property
//*************************************************************************
//
// IOHIDDevice_SetUInt32Property( inIOHIDDeviceRef, inKey, inValue )
//
// Purpose: convieance function to set a long property of an Device
//
// Inputs: inIOHIDDeviceRef - the Device
// inKey - CFString for the key
// inValue - the value to set it to
// Returns: nothing
//
#if 0 // unused
static void IOHIDDevice_SetUInt32Property(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, uint32_t inValue) {
CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue);
if ( tCFNumberRef ) {
IOHIDDeviceSetProperty(inIOHIDDeviceRef, inKey, tCFNumberRef);
CFRelease(tCFNumberRef);
}
} // IOHIDDevice_SetUInt32Property
#endif
//*************************************************************************
//
// IOHIDDevice_GetPtrProperty( inIOHIDDeviceRef, inKey, outValue )
//
// Purpose: convieance function to return a pointer property of a device
//
// Inputs: inIOHIDDeviceRef - the device
// inKey - CFString for the
// outValue - address where to restore the element
// Returns: the action cookie
// outValue - the device
//
static Boolean IOHIDDevice_GetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, void **outValue) {
Boolean result = FALSE;
if ( inIOHIDDeviceRef ) {
assert( IOHIDDeviceGetTypeID() == CFGetTypeID(inIOHIDDeviceRef) );
CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inIOHIDDeviceRef, inKey);
if ( tCFTypeRef ) {
// if this is a number
if ( CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef) ) {
// get it's value
#ifdef __LP64__
result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt64Type, outValue );
#else
result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, outValue );
#endif // ifdef __LP64__
}
}
}
return (result);
} // IOHIDDevice_GetPtrProperty
//*************************************************************************
//
// IOHIDDevice_SetPtrProperty( inIOHIDDeviceRef, inKey, inValue )
//
// Purpose: convieance function to set a long property of an Device
//
// Inputs: inIOHIDDeviceRef - the Device
// inKey - CFString for the key
// inValue - the value to set it to
// Returns: nothing
//
static void IOHIDDevice_SetPtrProperty(IOHIDDeviceRef inIOHIDDeviceRef, CFStringRef inKey, void *inValue) {
#ifdef __LP64__
CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &inValue);
#else
CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue);
#endif // ifdef __LP64__
if ( tCFNumberRef ) {
IOHIDDeviceSetProperty(inIOHIDDeviceRef, inKey, tCFNumberRef);
CFRelease(tCFNumberRef);
}
} // IOHIDDevice_SetPtrProperty
//*****************************************************
#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050

422
src/cocoa/IOHIDDevice_.h Normal file
View File

@ -0,0 +1,422 @@
// File: IOHIDDevice_.h
// Abstract: convieance functions for IOHIDDeviceGetProperty
// Version: 2.0 + 5.3
//
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
// Inc. ("Apple") in consideration of your agreement to the following
// terms, and your use, installation, modification or redistribution of
// this Apple software constitutes acceptance of these terms. If you do
// not agree with these terms, please do not use, install, modify or
// redistribute this Apple software.
//
// In consideration of your agreement to abide by the following terms, and
// subject to these terms, Apple grants you a personal, non-exclusive
// license, under Apple's copyrights in this original Apple software (the
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
// Software, with or without modifications, in source and/or binary forms;
// provided that if you redistribute the Apple Software in its entirety and
// without modifications, you must retain this notice and the following
// text and disclaimers in all such redistributions of the Apple Software.
// Neither the name, trademarks, service marks or logos of Apple Inc. may
// be used to endorse or promote products derived from the Apple Software
// without specific prior written permission from Apple. Except as
// expressly stated in this notice, no other rights or licenses, express or
// implied, are granted by Apple herein, including but not limited to any
// patent rights that may be infringed by your derivative works or by other
// works in which the Apple Software may be incorporated.
//
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (C) 2014 Apple Inc. All Rights Reserved.
//
//*****************************************************
#ifndef __IOHIDDevice__
#define __IOHIDDevice__
//*****************************************************
#pragma mark - includes & imports
#include <AvailabilityMacros.h>
#include "IOHIDLib_.h"
//*****************************************************
#if PRAGMA_ONCE
#pragma once
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if PRAGMA_IMPORT
#pragma import on
#endif
#if PRAGMA_STRUCT_ALIGN
#pragma options align=mac68k
#elif PRAGMA_STRUCT_PACKPUSH
#pragma pack(push, 2)
#elif PRAGMA_STRUCT_PACK
#pragma pack(2)
#endif
//*****************************************************
#pragma mark - typedef's, struct's, enums, defines, etc.
//-----------------------------------------------------
//*****************************************************
#pragma mark - exported globals
//-----------------------------------------------------
//*****************************************************
#pragma mark - exported function prototypes
//-----------------------------------------------------
//*************************************************************************
//
// HIDIsValidDevice( inIOHIDDeviceRef )
//
// Purpose: validate this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: Boolean - TRUE if we find the device in our( internal ) device list
//
extern Boolean HIDIsValidDevice(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetTransport( inIOHIDDeviceRef )
//
// Purpose: get the Transport CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFStringRef - the Transport CFString for this device
//
extern CFStringRef IOHIDDevice_GetTransport(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetVendorID( inIOHIDDeviceRef )
//
// Purpose: get the vendor ID for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the vendor ID for this device
//
extern uint32_t IOHIDDevice_GetVendorID(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetVendorIDSource( inIOHIDDeviceRef )
//
// Purpose: get the VendorIDSource for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the VendorIDSource for this device
//
extern uint32_t IOHIDDevice_GetVendorIDSource(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetProductID( inIOHIDDeviceRef )
//
// Purpose: get the product ID for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the product ID for this device
//
extern uint32_t IOHIDDevice_GetProductID(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetVersionNumber( inIOHIDDeviceRef )
//
// Purpose: get the VersionNumber CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the VersionNumber for this device
//
extern uint32_t IOHIDDevice_GetVersionNumber(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetManufacturer( inIOHIDDeviceRef )
//
// Purpose: get the Manufacturer CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFStringRef - the Manufacturer CFString for this device
//
extern CFStringRef IOHIDDevice_GetManufacturer(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetProduct( inIOHIDDeviceRef )
//
// Purpose: get the Product CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFStringRef - the Product CFString for this device
//
extern CFStringRef IOHIDDevice_GetProduct(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetSerialNumber( inIOHIDDeviceRef )
//
// Purpose: get the SerialNumber CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFStringRef - the SerialNumber CFString for this device
//
extern CFStringRef IOHIDDevice_GetSerialNumber(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetCountryCode( inIOHIDDeviceRef )
//
// Purpose: get the CountryCode CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the CountryCode for this device
//
extern uint32_t IOHIDDevice_GetCountryCode(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetLocationID( inIOHIDDeviceRef )
//
// Purpose: get the location ID for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the location ID for this device
//
extern uint32_t IOHIDDevice_GetLocationID(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetUsage( inIOHIDDeviceRef )
//
// Purpose: get the usage for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the usage for this device
//
extern uint32_t IOHIDDevice_GetUsage(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetUsagePage( inIOHIDDeviceRef )
//
// Purpose: get the usage page for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the usage page for this device
//
extern uint32_t IOHIDDevice_GetUsagePage(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetUsagePairs( inIOHIDDeviceRef )
//
// Purpose: get the UsagePairs CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFArrayRef - the UsagePairs for this device
//
extern CFArrayRef IOHIDDevice_GetUsagePairs(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetPrimaryUsage( inIOHIDDeviceRef )
//
// Purpose: get the PrimaryUsage CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFStringRef - the PrimaryUsage CFString for this device
//
extern uint32_t IOHIDDevice_GetPrimaryUsage(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetPrimaryUsagePage( inIOHIDDeviceRef )
//
// Purpose: get the PrimaryUsagePage CFString for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: CFStringRef - the PrimaryUsagePage CFString for this device
//
extern uint32_t IOHIDDevice_GetPrimaryUsagePage(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetMaxInputReportSize( inIOHIDDeviceRef )
//
// Purpose: get the MaxInputReportSize for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the MaxInputReportSize for this device
//
extern uint32_t IOHIDDevice_GetMaxInputReportSize(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetMaxOutputReportSize( inIOHIDDeviceRef )
//
// Purpose: get the MaxOutputReportSize for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the MaxOutputReportSize for this device
//
extern uint32_t IOHIDDevice_GetMaxOutputReportSize(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetMaxFeatureReportSize( inIOHIDDeviceRef )
//
// Purpose: get the MaxFeatureReportSize for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the MaxFeatureReportSize for this device
//
extern uint32_t IOHIDDevice_GetMaxFeatureReportSize(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetReportInterval( inIOHIDDeviceRef )
//
// Purpose: get the ReportInterval for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: uint32_t - the ReportInterval for this device
//
extern uint32_t IOHIDDevice_GetReportInterval(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_GetQueue( inIOHIDDeviceRef )
//
// Purpose: get the Queue for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: IOHIDQueueRef - the Queue for this device
//
extern IOHIDQueueRef IOHIDDevice_GetQueue(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_SetQueue( inIOHIDDeviceRef, inQueueRef )
//
// Purpose: Set the Queue for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
// inQueueRef - the Queue
//
// Returns: nothing
//
extern void IOHIDDevice_SetQueue(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDQueueRef inQueueRef);
//*************************************************************************
//
// IOHIDDevice_GetTransaction( inIOHIDDeviceRef )
//
// Purpose: get the Transaction for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
//
// Returns: IOHIDTransactionRef - the Transaction for this device
//
extern IOHIDTransactionRef IOHIDDevice_GetTransaction(IOHIDDeviceRef inIOHIDDeviceRef);
//*************************************************************************
//
// IOHIDDevice_SetTransaction( inIOHIDDeviceRef, inTransactionRef )
//
// Purpose: Set the Transaction for this device
//
// Inputs: inIOHIDDeviceRef - the IDHIDDeviceRef for this device
// inTransactionRef - the Transaction
//
// Returns: nothing
//
extern void IOHIDDevice_SetTransaction(IOHIDDeviceRef inIOHIDDeviceRef, IOHIDTransactionRef inTransactionRef);
//*****************************************************
#if PRAGMA_STRUCT_ALIGN
#pragma options align=reset
#elif PRAGMA_STRUCT_PACKPUSH
#pragma pack(pop)
#elif PRAGMA_STRUCT_PACK
#pragma pack()
#endif
#ifdef PRAGMA_IMPORT_OFF
#pragma import off
#elif PRAGMA_IMPORT
#pragma import reset
#endif
#ifdef __cplusplus
}
#endif
#endif // __IOHIDDevice__ //

509
src/cocoa/IOHIDElement_.c Normal file
View File

@ -0,0 +1,509 @@
// File: IOHIDElement_.c
// Abstract: convieance functions for IOHIDElementGetProperty
// Version: 2.0
//
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
// Inc. ("Apple") in consideration of your agreement to the following
// terms, and your use, installation, modification or redistribution of
// this Apple software constitutes acceptance of these terms. If you do
// not agree with these terms, please do not use, install, modify or
// redistribute this Apple software.
//
// In consideration of your agreement to abide by the following terms, and
// subject to these terms, Apple grants you a personal, non-exclusive
// license, under Apple's copyrights in this original Apple software (the
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
// Software, with or without modifications, in source and/or binary forms;
// provided that if you redistribute the Apple Software in its entirety and
// without modifications, you must retain this notice and the following
// text and disclaimers in all such redistributions of the Apple Software.
// Neither the name, trademarks, service marks or logos of Apple Inc. may
// be used to endorse or promote products derived from the Apple Software
// without specific prior written permission from Apple. Except as
// expressly stated in this notice, no other rights or licenses, express or
// implied, are granted by Apple herein, including but not limited to any
// patent rights that may be infringed by your derivative works or by other
// works in which the Apple Software may be incorporated.
//
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (C) 2009 Apple Inc. All Rights Reserved.
//
//*****************************************************
#include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
#pragma mark - includes & imports
//-----------------------------------------------------
#include "IOHIDElement_.h"
//*****************************************************
#pragma mark - typedef's, struct's, enums, defines, etc.
//-----------------------------------------------------
//*****************************************************
#pragma mark - local (static) function prototypes
//-----------------------------------------------------
// static Boolean IOHIDElement_GetLongProperty( IOHIDElementRef inElementRef, CFStringRef inKey, long * outValue );
// static void IOHIDElement_SetLongProperty( IOHIDElementRef inElementRef, CFStringRef inKey, long inValue );
//*****************************************************
#pragma mark - exported globals
//-----------------------------------------------------
//*****************************************************
#pragma mark - local (static) globals
//-----------------------------------------------------
//*****************************************************
#pragma mark - exported function implementations
//-----------------------------------------------------
//*************************************************************************
//
// HIDIsValidElement( inIOHIDElementRef )
//
// Purpose: validate this element
//
// Inputs: inIOHIDElementRef - the element
//
// Returns: Boolean - TRUE if this is a valid element ref
//
Boolean HIDIsValidElement(IOHIDElementRef inIOHIDElementRef) {
Boolean result = FALSE; // assume failure (pessimist!)
if ( inIOHIDElementRef ) {
if ( CFGetTypeID(inIOHIDElementRef) ==IOHIDElementGetTypeID() ) {
result = TRUE;
}
}
return (result);
} // HIDIsValidElement
//*************************************************************************
//
// IOHIDElement_GetValue( inElementRef, inIOHIDValueScaleType )
//
// Purpose: returns the current value for an element( polling )
//
// Notes: will return 0 on error conditions which should be accounted for by application
//
// Inputs: inElementRef - the element
// inIOHIDValueScaleType - scale type ( calibrated or physical )
//
// Returns: double - current value for element
//
double IOHIDElement_GetValue(IOHIDElementRef inElementRef, IOHIDValueScaleType inIOHIDValueScaleType) {
long result = 0;
IOHIDValueRef tIOHIDValueRef;
if ( kIOReturnSuccess == IOHIDDeviceGetValue(IOHIDElementGetDevice(inElementRef), inElementRef, &tIOHIDValueRef) ) {
result = IOHIDValueGetScaledValue(tIOHIDValueRef, inIOHIDValueScaleType);
}
return (result);
} // IOHIDElement_GetValue
//*************************************************************************
//
// IOHIDElement_GetCalibrationMin( inElementRef )
//
// Purpose: get the minimum bounds for a calibrated value for this element
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the minimum Calibration value for this element
//
CFIndex IOHIDElement_GetCalibrationMin(IOHIDElementRef inElementRef) {
CFIndex result;
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMinKey), &result) ) {
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMaxKey), &result) ) {
result = 0x7FFFFFFF;
}
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMinKey), result);
}
return (result);
} // IOHIDElement_GetCalibrationMin
//*************************************************************************
//
// IOHIDElement_SetCalibrationMin( inElementRef, inValue )
//
// Purpose: set the minimum bounds for a calibrated value for this element
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the minimum bounds for a calibrated value for this element
//
// Returns: nothing
//
void IOHIDElement_SetCalibrationMin(IOHIDElementRef inElementRef, CFIndex inValue) {
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMinKey), inValue);
} // IOHIDElement_SetCalibrationMin
//*************************************************************************
//
// IOHIDElement_GetCalibrationMax( inElementRef )
//
// Purpose: get the maximum bounds for a calibrated value for this element
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the maximum Calibration value for this element
//
CFIndex IOHIDElement_GetCalibrationMax(IOHIDElementRef inElementRef) {
CFIndex result;
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMaxKey), &result) ) {
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) {
result = -0x7FFFFFFF;
}
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMaxKey), result);
}
return (result);
} // IOHIDElement_GetCalibrationMax
//*************************************************************************
//
// IOHIDElement_SetCalibrationMax( inElementRef, inValue )
//
// Purpose: set the maximum bounds for a calibrated value for this element
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the maximum Calibration value for this element
//
// Returns: nothing
//
void IOHIDElement_SetCalibrationMax(IOHIDElementRef inElementRef, CFIndex inValue) {
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationMaxKey), inValue);
} // IOHIDElement_SetCalibrationMax
//*************************************************************************
//
// IOHIDElement_GetCalibrationSaturationMin( inElementRef )
//
// Purpose: get the mininum tolerance to be used when calibrating a logical element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the maximum Calibration value for this element
//
CFIndex IOHIDElement_GetCalibrationSaturationMin(IOHIDElementRef inElementRef) {
CFIndex result;
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMinKey), &result) ) {
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) {
result = -0x7FFFFFFF;
}
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMinKey), result);
}
return (result);
} // IOHIDElement_GetCalibrationSaturationMin
//*************************************************************************
//
// IOHIDElement_SetCalibrationSaturationMin( inElementRef, inValue )
//
// Purpose: set the mininum tolerance to be used when calibrating a logical element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the maximum Calibration value for this element
//
// Returns: nothing
//
void IOHIDElement_SetCalibrationSaturationMin(IOHIDElementRef inElementRef, CFIndex inValue) {
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMinKey), inValue);
} // IOHIDElement_SetCalibrationSaturationMin
//*************************************************************************
//
// IOHIDElement_GetCalibrationSaturationMax( inElementRef )
//
// Purpose: get the maximum tolerance to be used when calibrating a logical element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the maximum Calibration value for this element
//
CFIndex IOHIDElement_GetCalibrationSaturationMax(IOHIDElementRef inElementRef) {
CFIndex result;
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMaxKey), &result) ) {
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) {
result = -0x7FFFFFFF;
}
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMaxKey), result);
}
return (result);
} // IOHIDElement_GetCalibrationSaturationMax
//*************************************************************************
//
// IOHIDElement_SetCalibrationSaturationMax( inElementRef, inValue )
//
// Purpose: set the maximum tolerance to be used when calibrating a logical element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the maximum Calibration value for this element
//
// Returns: nothing
//
void IOHIDElement_SetCalibrationSaturationMax(IOHIDElementRef inElementRef, CFIndex inValue) {
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationSaturationMaxKey), inValue);
} // IOHIDElement_SetCalibrationSaturationMax
//*************************************************************************
//
// IOHIDElement_GetCalibrationDeadZoneMin( inElementRef )
//
// Purpose: get the minimum bounds near the midpoint of a logical value in which the value is ignored
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the maximum Calibration value for this element
//
CFIndex IOHIDElement_GetCalibrationDeadZoneMin(IOHIDElementRef inElementRef) {
CFIndex result;
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMinKey), &result) ) {
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) {
result = -0x7FFFFFFF;
}
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMinKey), result);
}
return (result);
} // IOHIDElement_GetCalibrationDeadZoneMin
//*************************************************************************
//
// IOHIDElement_SetCalibrationDeadZoneMin( inElementRef, inValue )
//
// Purpose: set the minimum bounds near the midpoint of a logical value in which the value is ignored
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the maximum Calibration value for this element
//
// Returns: nothing
//
void IOHIDElement_SetCalibrationDeadZoneMin(IOHIDElementRef inElementRef, CFIndex inValue) {
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMinKey), inValue);
} // IOHIDElement_SetCalibrationDeadZoneMin
//*************************************************************************
//
// IOHIDElement_GetCalibrationDeadZoneMax( inElementRef )
//
// Purpose: get the maximum bounds near the midpoint of a logical value in which the value is ignored
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the maximum Calibration value for this element
//
CFIndex IOHIDElement_GetCalibrationDeadZoneMax(IOHIDElementRef inElementRef) {
CFIndex result;
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMaxKey), &result) ) {
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) {
result = -0x7FFFFFFF;
}
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMaxKey), result);
}
return (result);
} // IOHIDElement_GetCalibrationDeadZoneMax
//*************************************************************************
//
// IOHIDElement_SetCalibrationDeadZoneMax( inElementRef, inValue )
//
// Purpose: set the maximum bounds near the midpoint of a logical value in which the value is ignored
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the maximum Calibration value for this element
//
// Returns: nothing
//
void IOHIDElement_SetCalibrationDeadZoneMax(IOHIDElementRef inElementRef, CFIndex inValue) {
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationDeadZoneMaxKey), inValue);
} // IOHIDElement_SetCalibrationDeadZoneMax
//*************************************************************************
//
// IOHIDElement_GetCalibrationGranularity( inElementRef )
//
// Purpose: get the level of detail returned for a calibrated element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: double_t - the maximum Calibration value for this element
//
double_t IOHIDElement_GetCalibrationGranularity(IOHIDElementRef inElementRef) {
CFIndex result;
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationGranularityKey), &result) ) {
if ( !IOHIDElement_GetLongProperty(inElementRef, CFSTR(kIOHIDElementMinKey), &result) ) {
result = -0x7FFFFFFF;
}
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationGranularityKey), result);
}
return (result);
} // IOHIDElement_GetCalibrationGranularity
//*************************************************************************
//
// IOHIDElement_SetCalibrationGranularity( inElementRef, inValue )
//
// Purpose: set the level of detail returned for a calibrated element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the the level of detail for this element
//
// Returns: nothing
//
void IOHIDElement_SetCalibrationGranularity(IOHIDElementRef inElementRef, double_t inValue) {
IOHIDElement_SetLongProperty(inElementRef, CFSTR(kIOHIDElementCalibrationGranularityKey), inValue);
} // IOHIDElement_SetCalibrationGranularity
//*************************************************************************
//
// IOHIDElement_SetupCalibration( inElementRef )
//
// Purpose: set default values for the element calibration parameters
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: nothing
//
void IOHIDElement_SetupCalibration(IOHIDElementRef inIOHIDElementRef) {
// these are the min/max values returned by IOHIDValueGetScaledValue( v, kIOHIDValueScaleTypeCalibrated );
IOHIDElement_SetCalibrationMin( inIOHIDElementRef, IOHIDElementGetLogicalMin(inIOHIDElementRef) );
IOHIDElement_SetCalibrationMax( inIOHIDElementRef, IOHIDElementGetLogicalMax(inIOHIDElementRef) );
// this is the granularity of the values returned by IOHIDValueGetScaledValue( v, kIOHIDValueScaleTypeCalibrated );
// for example if set to 0.1 the values returned will be multiples of 0.1 ( 0.1, 0.2, 0.3, etc. )
IOHIDElement_SetCalibrationGranularity(inIOHIDElementRef, 0.);
// these define the dead zone (like in the middel of joystick axis)
IOHIDElement_SetCalibrationDeadZoneMin(inIOHIDElementRef, 0);
IOHIDElement_SetCalibrationDeadZoneMax(inIOHIDElementRef, 0);
#if 1
// get the current value of this element
double value = IOHIDElement_GetValue(inIOHIDElementRef, kIOHIDValueScaleTypePhysical);
// use it as our min/mas saturation
IOHIDElement_SetCalibrationSaturationMin(inIOHIDElementRef, value);
IOHIDElement_SetCalibrationSaturationMax(inIOHIDElementRef, value);
#else
// calculate the middle physical value we would expect from this element
CFIndex valueMin = IOHIDElementGetPhysicalMin(inIOHIDElementRef);
CFIndex valueMax = IOHIDElementGetPhysicalMax(inIOHIDElementRef);
CFIndex valueMid = (valueMin + valueMax) / 2;
// use it as our min/mas saturation
// this value determines the min/max values that have been recieved from the device element
IOHIDElement_SetCalibrationSaturationMin(inIOHIDElementRef, valueMid);
IOHIDElement_SetCalibrationSaturationMax(inIOHIDElementRef, valueMid);
// get the current value of this element
double value = IOHIDElement_GetValue(inIOHIDElementRef, kIOHIDValueScaleTypePhysical);
// and use it to adjust the current saturation values if it's outside their range
if ( value < IOHIDElement_GetCalibrationSaturationMin(inIOHIDElementRef) ) {
IOHIDElement_SetCalibrationSaturationMin(inIOHIDElementRef, value);
}
if ( value > IOHIDElement_GetCalibrationSaturationMax(inIOHIDElementRef) ) {
IOHIDElement_SetCalibrationSaturationMax(inIOHIDElementRef, value);
}
#endif
} // IOHIDElement_SetupCalibration
//*****************************************************
#pragma mark - local (static) function implementations
//-----------------------------------------------------
//*************************************************************************
//
// IOHIDElement_GetLongProperty( inElementRef, inKey, outValue )
//
// Purpose: convieance function to return a long property of an element
//
// Inputs: inElementRef - the element
// inKey - CFString for the key
// outValue - address where to store the value
// Returns: Boolean - TRUE if successful
// outValue - the long property's value
//
Boolean IOHIDElement_GetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long *outValue) {
Boolean result = FALSE;
CFTypeRef tCFTypeRef = IOHIDElementGetProperty(inElementRef, inKey);
if ( tCFTypeRef ) {
// if this is a number
if ( CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef) ) {
// get it's value
result = CFNumberGetValue( (CFNumberRef) tCFTypeRef, kCFNumberSInt32Type, outValue );
}
}
return (result);
} /* IOHIDElement_GetLongProperty */
//*************************************************************************
//
// IOHIDElement_SetLongProperty( inElementRef, inKey, inValue )
//
// Purpose: convieance function to set a long property of an element
//
// Inputs: inElementRef - the element
// inKey - CFString for the key
// inValue - the value to set it to
//
// Returns: nothing
//
void IOHIDElement_SetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long inValue) {
CFNumberRef tCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &inValue);
if ( tCFNumberRef ) {
IOHIDElementSetProperty(inElementRef, inKey, tCFNumberRef);
CFRelease(tCFNumberRef);
}
} // IOHIDElement_SetLongProperty
//*****************************************************
#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050

339
src/cocoa/IOHIDElement_.h Normal file
View File

@ -0,0 +1,339 @@
// File: IOHIDElement_.h
// Abstract: convieance functions for IOHIDElementGetProperty
// Version: 2.0
//
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
// Inc. ("Apple") in consideration of your agreement to the following
// terms, and your use, installation, modification or redistribution of
// this Apple software constitutes acceptance of these terms. If you do
// not agree with these terms, please do not use, install, modify or
// redistribute this Apple software.
//
// In consideration of your agreement to abide by the following terms, and
// subject to these terms, Apple grants you a personal, non-exclusive
// license, under Apple's copyrights in this original Apple software (the
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
// Software, with or without modifications, in source and/or binary forms;
// provided that if you redistribute the Apple Software in its entirety and
// without modifications, you must retain this notice and the following
// text and disclaimers in all such redistributions of the Apple Software.
// Neither the name, trademarks, service marks or logos of Apple Inc. may
// be used to endorse or promote products derived from the Apple Software
// without specific prior written permission from Apple. Except as
// expressly stated in this notice, no other rights or licenses, express or
// implied, are granted by Apple herein, including but not limited to any
// patent rights that may be infringed by your derivative works or by other
// works in which the Apple Software may be incorporated.
//
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (C) 2009 Apple Inc. All Rights Reserved.
//
//*****************************************************
#ifndef __IOHIDElement___
#define __IOHIDElement___
//*****************************************************
#pragma mark - includes & imports
#include <AvailabilityMacros.h>
#include "IOHIDLib_.h"
//*****************************************************
#if PRAGMA_ONCE
#pragma once
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if PRAGMA_IMPORT
#pragma import on
#endif
#if PRAGMA_STRUCT_ALIGN
#pragma options align=mac68k
#elif PRAGMA_STRUCT_PACKPUSH
#pragma pack(push, 2)
#elif PRAGMA_STRUCT_PACK
#pragma pack(2)
#endif
//*****************************************************
#pragma mark - typedef's, struct's, enums, defines, etc.
//-----------------------------------------------------
//*****************************************************
#pragma mark - exported globals
//-----------------------------------------------------
//*****************************************************
#pragma mark - exported function prototypes
//-----------------------------------------------------
//*************************************************************************
//
// HIDIsValidElement( inIOHIDElementRef )
//
// Purpose: validate this element
//
// Inputs: inIOHIDElementRef - the element
//
// Returns: Boolean - TRUE if this is a valid element ref
//
extern Boolean HIDIsValidElement(IOHIDElementRef inIOHIDElementRef);
//*************************************************************************
//
// IOHIDElement_GetValue( inElementRef, inIOHIDValueScaleType )
//
// Purpose: returns the current value for an element( polling )
//
// Notes: will return 0 on error conditions which should be accounted for by application
//
// Inputs: inElementRef - the element
// inIOHIDValueScaleType - scale type ( calibrated or physical )
//
// Returns: double - current value for element
//
extern double IOHIDElement_GetValue(IOHIDElementRef inElementRef, IOHIDValueScaleType inIOHIDValueScaleType);
//*************************************************************************
//
// IOHIDElement_GetCalibrationMin( inElementRef )
//
// Purpose: get the minimum bounds for a calibrated value for this element
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the minimum Calibration value for this element
//
extern CFIndex IOHIDElement_GetCalibrationMin(IOHIDElementRef inElementRef);
//*************************************************************************
//
// IOHIDElement_SetCalibrationMin( inElementRef, inValue )
//
// Purpose: set the minimum bounds for a calibrated value for this element
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the minimum bounds for a calibrated value for this element
//
// Returns: nothing
//
extern void IOHIDElement_SetCalibrationMin(IOHIDElementRef inElementRef, CFIndex inValue);
//*************************************************************************
//
// IOHIDElement_GetCalibrationMax( inElementRef )
//
// Purpose: get the maximum bounds for a calibrated value for this element
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the maximum Calibration value for this element
//
extern CFIndex IOHIDElement_GetCalibrationMax(IOHIDElementRef inElementRef);
//*************************************************************************
//
// IOHIDElement_SetCalibrationMax( inElementRef, inValue )
//
// Purpose: set the maximum bounds for a calibrated value for this element
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the maximum Calibration value for this element
//
// Returns: nothing
//
extern void IOHIDElement_SetCalibrationMax(IOHIDElementRef inElementRef, CFIndex inValue);
//*************************************************************************
//
// IOHIDElement_GetCalibrationSaturationMin( inElementRef )
//
// Purpose: get the mininum tolerance to be used when calibrating a logical element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the maximum Calibration value for this element
//
extern CFIndex IOHIDElement_GetCalibrationSaturationMin(IOHIDElementRef inElementRef);
//*************************************************************************
//
// IOHIDElement_SetCalibrationSaturationMin( inElementRef, inValue )
//
// Purpose: set the mininum tolerance to be used when calibrating a logical element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the maximum Calibration value for this element
//
// Returns: nothing
//
extern void IOHIDElement_SetCalibrationSaturationMin(IOHIDElementRef inElementRef, CFIndex inValue);
//*************************************************************************
//
// IOHIDElement_GetCalibrationSaturationMax( inElementRef )
//
// Purpose: get the maximum tolerance to be used when calibrating a logical element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the maximum Calibration value for this element
//
extern CFIndex IOHIDElement_GetCalibrationSaturationMax(IOHIDElementRef inElementRef);
//*************************************************************************
//
// IOHIDElement_SetCalibrationSaturationMax( inElementRef, inValue )
//
// Purpose: set the maximum tolerance to be used when calibrating a logical element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the maximum Calibration value for this element
//
// Returns: nothing
//
extern void IOHIDElement_SetCalibrationSaturationMax(IOHIDElementRef inElementRef, CFIndex inValue);
//*************************************************************************
//
// IOHIDElement_GetCalibrationDeadZoneMin( inElementRef )
//
// Purpose: get the minimum bounds near the midpoint of a logical value in which the value is ignored
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the maximum Calibration value for this element
//
extern CFIndex IOHIDElement_GetCalibrationDeadZoneMin(IOHIDElementRef inElementRef);
//*************************************************************************
//
// IOHIDElement_SetCalibrationDeadZoneMin( inElementRef, inValue )
//
// Purpose: set the minimum bounds near the midpoint of a logical value in which the value is ignored
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the maximum Calibration value for this element
//
// Returns: nothing
//
extern void IOHIDElement_SetCalibrationDeadZoneMin(IOHIDElementRef inElementRef, CFIndex inValue);
//*************************************************************************
//
// IOHIDElement_GetCalibrationDeadZoneMax( inElementRef )
//
// Purpose: get the maximum bounds near the midpoint of a logical value in which the value is ignored
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: CFIndex - the maximum Calibration value for this element
//
extern CFIndex IOHIDElement_GetCalibrationDeadZoneMax(IOHIDElementRef inElementRef);
//*************************************************************************
//
// IOHIDElement_SetCalibrationDeadZoneMax( inElementRef, inValue )
//
// Purpose: set the maximum bounds near the midpoint of a logical value in which the value is ignored
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the maximum Calibration value for this element
//
// Returns: nothing
//
extern void IOHIDElement_SetCalibrationDeadZoneMax(IOHIDElementRef inElementRef, CFIndex inValue);
//*************************************************************************
//
// IOHIDElement_GetCalibrationGranularity( inElementRef )
//
// Purpose: get the level of detail returned for a calibrated element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: double_t - the maximum Calibration value for this element
//
extern double_t IOHIDElement_GetCalibrationGranularity(IOHIDElementRef inElementRef);
//*************************************************************************
//
// IOHIDElement_SetCalibrationGranularity( inElementRef, inValue )
//
// Purpose: set the level of detail returned for a calibrated element value
//
// Inputs: inElementRef - the IOHIDElementRef for this element
// inValue - the the level of detail for this element
//
// Returns: nothing
//
extern void IOHIDElement_SetCalibrationGranularity(IOHIDElementRef inElementRef, double_t inValue);
//*************************************************************************
//
// IOHIDElement_SetupCalibration( inElementRef )
//
// Purpose: set default values for the element calibration parameters
//
// Inputs: inElementRef - the IOHIDElementRef for this element
//
// Returns: nothing
//
extern void IOHIDElement_SetupCalibration(IOHIDElementRef inIOHIDElementRef);
extern Boolean IOHIDElement_GetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long *outValue);
extern void IOHIDElement_SetLongProperty(IOHIDElementRef inElementRef, CFStringRef inKey, long inValue);
//*****************************************************
#if PRAGMA_STRUCT_ALIGN
#pragma options align=reset
#elif PRAGMA_STRUCT_PACKPUSH
#pragma pack(pop)
#elif PRAGMA_STRUCT_PACK
#pragma pack()
#endif
#ifdef PRAGMA_IMPORT_OFF
#pragma import off
#elif PRAGMA_IMPORT
#pragma import reset
#endif
#ifdef __cplusplus
}
#endif
#endif // __IOHIDElement___ //

111
src/cocoa/IOHIDLib_.h Normal file
View File

@ -0,0 +1,111 @@
// File: IOHIDLib_.h
// Abstract: Single include file for all header files of IOHIDLib
// Version: 2.0
//
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
// Inc. ("Apple") in consideration of your agreement to the following
// terms, and your use, installation, modification or redistribution of
// this Apple software constitutes acceptance of these terms. If you do
// not agree with these terms, please do not use, install, modify or
// redistribute this Apple software.
//
// In consideration of your agreement to abide by the following terms, and
// subject to these terms, Apple grants you a personal, non-exclusive
// license, under Apple's copyrights in this original Apple software (the
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
// Software, with or without modifications, in source and/or binary forms;
// provided that if you redistribute the Apple Software in its entirety and
// without modifications, you must retain this notice and the following
// text and disclaimers in all such redistributions of the Apple Software.
// Neither the name, trademarks, service marks or logos of Apple Inc. may
// be used to endorse or promote products derived from the Apple Software
// without specific prior written permission from Apple. Except as
// expressly stated in this notice, no other rights or licenses, express or
// implied, are granted by Apple herein, including but not limited to any
// patent rights that may be infringed by your derivative works or by other
// works in which the Apple Software may be incorporated.
//
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (C) 2009 Apple Inc. All Rights Reserved.
//
//*****************************************************
#ifndef __IOHIDLib___
#define __IOHIDLib___
//*****************************************************
#pragma mark - includes & imports
//-----------------------------------------------------
#include <IOKit/hid/IOHIDLib.h>
#include "IOHIDDevice_.h"
#include "IOHIDElement_.h"
#include "ImmrHIDUtilAddOn.h"
//*****************************************************
#if PRAGMA_ONCE
#pragma once
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if PRAGMA_IMPORT
#pragma import on
#endif
#if PRAGMA_STRUCT_ALIGN
#pragma options align=mac68k
#elif PRAGMA_STRUCT_PACKPUSH
#pragma pack(push, 2)
#elif PRAGMA_STRUCT_PACK
#pragma pack(2)
#endif
//*****************************************************
#pragma mark - typedef's, struct's, enums, defines, etc.
//-----------------------------------------------------
//*****************************************************
#pragma mark - exported globals
//-----------------------------------------------------
//*****************************************************
#pragma mark - exported function prototypes
//-----------------------------------------------------
//*****************************************************
#if PRAGMA_STRUCT_ALIGN
#pragma options align=reset
#elif PRAGMA_STRUCT_PACKPUSH
#pragma pack(pop)
#elif PRAGMA_STRUCT_PACK
#pragma pack()
#endif
#ifdef PRAGMA_IMPORT_OFF
#pragma import off
#elif PRAGMA_IMPORT
#pragma import reset
#endif
#ifdef __cplusplus
}
#endif
#endif // __IOHIDLib___

View File

@ -0,0 +1,108 @@
// File: ImmrHIDUtilAddOn.c
// Abstract: Glue code to convert IOHIDDeviceRef's to (FFB) io_object_t's
// Version: 2.0
//
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
// Inc. ("Apple") in consideration of your agreement to the following
// terms, and your use, installation, modification or redistribution of
// this Apple software constitutes acceptance of these terms. If you do
// not agree with these terms, please do not use, install, modify or
// redistribute this Apple software.
//
// In consideration of your agreement to abide by the following terms, and
// subject to these terms, Apple grants you a personal, non-exclusive
// license, under Apple's copyrights in this original Apple software (the
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
// Software, with or without modifications, in source and/or binary forms;
// provided that if you redistribute the Apple Software in its entirety and
// without modifications, you must retain this notice and the following
// text and disclaimers in all such redistributions of the Apple Software.
// Neither the name, trademarks, service marks or logos of Apple Inc. may
// be used to endorse or promote products derived from the Apple Software
// without specific prior written permission from Apple. Except as
// expressly stated in this notice, no other rights or licenses, express or
// implied, are granted by Apple herein, including but not limited to any
// patent rights that may be infringed by your derivative works or by other
// works in which the Apple Software may be incorporated.
//
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (C) 2009 Apple Inc. All Rights Reserved.
//
//*****************************************************
#include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
#include <mach/mach.h>
#include <mach/mach_error.h>
#include "ImmrHIDUtilAddOn.h"
//---------------------------------------------------------------------------------
//
// AllocateHIDObjectFromIOHIDDeviceRef( )
//
// returns:
// NULL, or acceptable io_object_t
//
//---------------------------------------------------------------------------------
io_service_t AllocateHIDObjectFromIOHIDDeviceRef(IOHIDDeviceRef inIOHIDDeviceRef) {
io_service_t result = 0L;
if ( inIOHIDDeviceRef ) {
// Set up the matching criteria for the devices we're interested in.
// We are interested in instances of class IOHIDDevice.
// matchingDict is consumed below( in IOServiceGetMatchingService )
// so we have no leak here.
CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOHIDDeviceKey);
if ( matchingDict ) {
// Add a key for locationID to our matching dictionary. This works for matching to
// IOHIDDevices, so we will only look for a device attached to that particular port
// on the machine.
CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty( inIOHIDDeviceRef, CFSTR(kIOHIDLocationIDKey) );
if ( tCFTypeRef ) {
CFDictionaryAddValue(matchingDict, CFSTR(kIOHIDLocationIDKey), tCFTypeRef);
// CFRelease( tCFTypeRef ); // don't release objects that we "Get".
// IOServiceGetMatchingService assumes that we already know that there is only one device
// that matches. This way we don't have to do the whole iteration dance to look at each
// device that matches. This is a new API in 10.2
result = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
}
// Note: We're not leaking the matchingDict.
// One reference is consumed by IOServiceGetMatchingServices
}
}
return (result);
} // AllocateHIDObjectFromIOHIDDeviceRef
//---------------------------------------------------------------------------------
//
// FreeHIDObject( )
//
//---------------------------------------------------------------------------------
bool FreeHIDObject(io_service_t inHIDObject) {
kern_return_t kr;
kr = IOObjectRelease(inHIDObject);
return (kIOReturnSuccess == kr);
} // FreeHIDObject
#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1050

View File

@ -0,0 +1,50 @@
// File: ImmrHIDUtilAddOn.h
// Abstract: Glue code to convert IOHIDDeviceRef's to (FFB) io_object_t's
// Version: 2.0
//
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
// Inc. ("Apple") in consideration of your agreement to the following
// terms, and your use, installation, modification or redistribution of
// this Apple software constitutes acceptance of these terms. If you do
// not agree with these terms, please do not use, install, modify or
// redistribute this Apple software.
//
// In consideration of your agreement to abide by the following terms, and
// subject to these terms, Apple grants you a personal, non-exclusive
// license, under Apple's copyrights in this original Apple software (the
// "Apple Software"), to use, reproduce, modify and redistribute the Apple
// Software, with or without modifications, in source and/or binary forms;
// provided that if you redistribute the Apple Software in its entirety and
// without modifications, you must retain this notice and the following
// text and disclaimers in all such redistributions of the Apple Software.
// Neither the name, trademarks, service marks or logos of Apple Inc. may
// be used to endorse or promote products derived from the Apple Software
// without specific prior written permission from Apple. Except as
// expressly stated in this notice, no other rights or licenses, express or
// implied, are granted by Apple herein, including but not limited to any
// patent rights that may be infringed by your derivative works or by other
// works in which the Apple Software may be incorporated.
//
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE
// MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
// THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
// OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
// MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
// AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
// STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (C) 2009 Apple Inc. All Rights Reserved.
//
//*****************************************************
#include <IOKit/IOKitLib.h>
#include <IOKit/hid/IOHIDLib.h>
extern io_service_t AllocateHIDObjectFromIOHIDDeviceRef(IOHIDDeviceRef inIOHIDDeviceRef);
extern bool FreeHIDObject(io_object_t inHIDObject);

2179
src/cocoa/i_backend_cocoa.mm Normal file

File diff suppressed because it is too large Load Diff

830
src/cocoa/i_joystick.cpp Normal file
View File

@ -0,0 +1,830 @@
/*
** i_joystick.cpp
**
**---------------------------------------------------------------------------
** Copyright 2012-2014 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "m_joy.h"
#include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
#include "HID_Utilities_External.h"
#include "d_event.h"
#include "doomdef.h"
#include "templates.h"
#include "i_osversion.h"
namespace
{
FString ToFString( const CFStringRef string )
{
if ( NULL == string )
{
return FString();
}
const CFIndex stringLength = CFStringGetLength( string );
if ( 0 == stringLength )
{
return FString();
}
const size_t bufferSize = CFStringGetMaximumSizeForEncoding( stringLength, kCFStringEncodingUTF8 ) + 1;
char buffer[ bufferSize ];
memset( buffer, 0, bufferSize );
CFStringGetCString( string, buffer, bufferSize, kCFStringEncodingUTF8 );
return FString( buffer );
}
class IOKitJoystick : public IJoystickConfig
{
public:
explicit IOKitJoystick( IOHIDDeviceRef device );
virtual ~IOKitJoystick();
virtual FString GetName();
virtual float GetSensitivity();
virtual void SetSensitivity( float scale );
virtual int GetNumAxes();
virtual float GetAxisDeadZone( int axis );
virtual EJoyAxis GetAxisMap( int axis );
virtual const char* GetAxisName( int axis );
virtual float GetAxisScale( int axis );
virtual void SetAxisDeadZone( int axis, float deadZone );
virtual void SetAxisMap( int axis, EJoyAxis gameAxis );
virtual void SetAxisScale( int axis, float scale );
virtual bool IsSensitivityDefault();
virtual bool IsAxisDeadZoneDefault( int axis );
virtual bool IsAxisMapDefault( int axis );
virtual bool IsAxisScaleDefault( int axis );
virtual void SetDefaultConfig();
virtual FString GetIdentifier();
void AddAxes( float axes[ NUM_JOYAXIS ] ) const;
void Update();
private:
IOHIDDeviceRef m_device;
float m_sensitivity;
struct AxisInfo
{
char name[ 64 ];
float value;
float deadZone;
float defaultDeadZone;
float sensitivity;
float defaultSensitivity;
EJoyAxis gameAxis;
EJoyAxis defaultGameAxis;
IOHIDElementRef element;
};
TArray< AxisInfo > m_axes;
TArray< IOHIDElementRef > m_buttons;
TArray< IOHIDElementRef > m_POVs;
static const float DEFAULT_DEADZONE;
static const float DEFAULT_SENSITIVITY;
bool ProcessAxis ( const IOHIDValueRef value );
bool ProcessButton( const IOHIDValueRef value );
bool ProcessPOV ( const IOHIDValueRef value );
};
const float IOKitJoystick::DEFAULT_DEADZONE = 0.25f;
const float IOKitJoystick::DEFAULT_SENSITIVITY = 1.0f;
IOKitJoystick::IOKitJoystick( IOHIDDeviceRef device )
: m_device( device )
, m_sensitivity( DEFAULT_SENSITIVITY )
{
assert(NULL != device);
assert(IOHIDDeviceGetTypeID() == CFGetTypeID(device));
CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
assert(NULL != elements);
assert(CFArrayGetTypeID() == CFGetTypeID(elements));
for (CFIndex i = 0, count = CFArrayGetCount(elements); i < count; ++i)
{
const IOHIDElementRef element =
static_cast<IOHIDElementRef>(const_cast<void*>(CFArrayGetValueAtIndex(elements, i)));
assert(NULL != element);
assert(IOHIDElementGetTypeID() == CFGetTypeID(element));
const uint32_t usagePage = IOHIDElementGetUsagePage( element );
if ( kHIDPage_GenericDesktop == usagePage )
{
const uint32_t usage = IOHIDElementGetUsage( element );
if ( kHIDUsage_GD_Slider == usage
|| kHIDUsage_GD_X == usage || kHIDUsage_GD_Y == usage || kHIDUsage_GD_Z == usage
|| kHIDUsage_GD_Rx == usage || kHIDUsage_GD_Ry == usage || kHIDUsage_GD_Rz == usage )
{
AxisInfo axis;
memset( &axis, 0, sizeof( axis ) );
if ( const CFStringRef name = IOHIDElementGetName( element ) )
{
CFStringGetCString( name, axis.name, sizeof( axis.name ) - 1, kCFStringEncodingUTF8 );
}
else
{
snprintf( axis.name, sizeof( axis.name ), "Axis %i", m_axes.Size() + 1 );
}
axis.element = element;
m_axes.Push( axis );
IOHIDElement_SetCalibrationMin( element, -1 );
IOHIDElement_SetCalibrationMax( element, 1 );
HIDQueueElement( m_device, element );
}
else if ( kHIDUsage_GD_Hatswitch == usage && m_POVs.Size() < 4 )
{
m_POVs.Push( element );
HIDQueueElement( m_device, element );
}
}
else if ( kHIDPage_Button == usagePage )
{
m_buttons.Push( element );
HIDQueueElement( m_device, element );
}
}
CFRelease(elements);
SetDefaultConfig();
}
IOKitJoystick::~IOKitJoystick()
{
M_SaveJoystickConfig( this );
}
FString IOKitJoystick::GetName()
{
FString result;
result += ToFString( IOHIDDevice_GetManufacturer( m_device ) );
result += " ";
result += ToFString( IOHIDDevice_GetProduct( m_device ) );
return result;
}
float IOKitJoystick::GetSensitivity()
{
return m_sensitivity;
}
void IOKitJoystick::SetSensitivity( float scale )
{
m_sensitivity = scale;
}
int IOKitJoystick::GetNumAxes()
{
return static_cast< int >( m_axes.Size() );
}
#define IS_AXIS_VALID ( static_cast< unsigned int >( axis ) < m_axes.Size() )
float IOKitJoystick::GetAxisDeadZone( int axis )
{
return IS_AXIS_VALID ? m_axes[ axis ].deadZone : 0.0f;
}
EJoyAxis IOKitJoystick::GetAxisMap( int axis )
{
return IS_AXIS_VALID ? m_axes[ axis ].gameAxis : JOYAXIS_None;
}
const char* IOKitJoystick::GetAxisName( int axis )
{
return IS_AXIS_VALID ? m_axes[ axis ].name : "Invalid";
}
float IOKitJoystick::GetAxisScale( int axis )
{
return IS_AXIS_VALID ? m_axes[ axis ].sensitivity : 0.0f;
}
void IOKitJoystick::SetAxisDeadZone( int axis, float deadZone )
{
if ( IS_AXIS_VALID )
{
m_axes[ axis ].deadZone = clamp( deadZone, 0.0f, 1.0f );
}
}
void IOKitJoystick::SetAxisMap( int axis, EJoyAxis gameAxis )
{
if ( IS_AXIS_VALID )
{
m_axes[ axis ].gameAxis = ( gameAxis > JOYAXIS_None && gameAxis < NUM_JOYAXIS )
? gameAxis
: JOYAXIS_None;
}
}
void IOKitJoystick::SetAxisScale( int axis, float scale )
{
if ( IS_AXIS_VALID )
{
m_axes[ axis ].sensitivity = scale;
}
}
bool IOKitJoystick::IsSensitivityDefault()
{
return DEFAULT_SENSITIVITY == m_sensitivity;
}
bool IOKitJoystick::IsAxisDeadZoneDefault( int axis )
{
return IS_AXIS_VALID
? ( m_axes[ axis ].deadZone == m_axes[ axis ].defaultDeadZone )
: true;
}
bool IOKitJoystick::IsAxisMapDefault( int axis )
{
return IS_AXIS_VALID
? ( m_axes[ axis ].gameAxis == m_axes[ axis ].defaultGameAxis )
: true;
}
bool IOKitJoystick::IsAxisScaleDefault( int axis )
{
return IS_AXIS_VALID
? ( m_axes[ axis ].sensitivity == m_axes[ axis ].defaultSensitivity )
: true;
}
#undef IS_AXIS_VALID
void IOKitJoystick::SetDefaultConfig()
{
m_sensitivity = DEFAULT_SENSITIVITY;
const size_t axisCount = m_axes.Size();
for ( size_t i = 0; i < axisCount; ++i )
{
m_axes[i].deadZone = DEFAULT_DEADZONE;
m_axes[i].sensitivity = DEFAULT_SENSITIVITY;
m_axes[i].gameAxis = JOYAXIS_None;
}
// Two axes? Horizontal is yaw and vertical is forward.
if ( 2 == axisCount)
{
m_axes[0].gameAxis = JOYAXIS_Yaw;
m_axes[1].gameAxis = JOYAXIS_Forward;
}
// Three axes? First two are movement, third is yaw.
else if ( axisCount >= 3 )
{
m_axes[0].gameAxis = JOYAXIS_Side;
m_axes[1].gameAxis = JOYAXIS_Forward;
m_axes[2].gameAxis = JOYAXIS_Yaw;
// Four axes? First two are movement, last two are looking around.
if ( axisCount >= 4 )
{
m_axes[3].gameAxis = JOYAXIS_Pitch;
// ??? m_axes[3].sensitivity = 0.75f;
// Five axes? Use the fifth one for moving up and down.
if ( axisCount >= 5 )
{
m_axes[4].gameAxis = JOYAXIS_Up;
}
}
}
// If there is only one axis, then we make no assumptions about how
// the user might want to use it.
// Preserve defaults for config saving.
for ( size_t i = 0; i < axisCount; ++i )
{
m_axes[i].defaultDeadZone = m_axes[i].deadZone;
m_axes[i].defaultSensitivity = m_axes[i].sensitivity;
m_axes[i].defaultGameAxis = m_axes[i].gameAxis;
}
}
FString IOKitJoystick::GetIdentifier()
{
char identifier[ 32 ] = {0};
snprintf( identifier, sizeof( identifier ), "VID_%04x_PID_%04x",
IOHIDDevice_GetVendorID( m_device ), IOHIDDevice_GetProductID( m_device ) );
return FString( identifier );
}
void IOKitJoystick::AddAxes( float axes[ NUM_JOYAXIS ] ) const
{
for ( size_t i = 0, count = m_axes.Size(); i < count; ++i )
{
const EJoyAxis axis = m_axes[i].gameAxis;
if ( JOYAXIS_None == axis )
{
continue;
}
axes[ axis ] -= m_axes[i].value;
}
}
void IOKitJoystick::Update()
{
IOHIDValueRef value = NULL;
while ( HIDGetEvent( m_device, &value ) && NULL != value )
{
ProcessAxis( value ) || ProcessButton( value ) || ProcessPOV( value );
CFRelease( value );
}
}
bool IOKitJoystick::ProcessAxis( const IOHIDValueRef value )
{
const IOHIDElementRef element = IOHIDValueGetElement( value );
if ( NULL == element )
{
return false;
}
for ( size_t i = 0, count = m_axes.Size(); i < count; ++i )
{
if ( element != m_axes[i].element )
{
continue;
}
AxisInfo& axis = m_axes[i];
const double scaledValue = IOHIDValueGetScaledValue( value, kIOHIDValueScaleTypeCalibrated );
const double filteredValue = Joy_RemoveDeadZone( scaledValue, axis.deadZone, NULL );
axis.value = static_cast< float >( filteredValue * m_sensitivity * axis.sensitivity );
return true;
}
return false;
}
bool IOKitJoystick::ProcessButton( const IOHIDValueRef value )
{
const IOHIDElementRef element = IOHIDValueGetElement( value );
if ( NULL == element )
{
return false;
}
for ( size_t i = 0, count = m_buttons.Size(); i < count; ++i )
{
if ( element != m_buttons[i] )
{
continue;
}
const int newButton = IOHIDValueGetIntegerValue( value ) & 1;
const int oldButton = ~newButton;
Joy_GenerateButtonEvents( oldButton, newButton, 1,
static_cast< int >( KEY_FIRSTJOYBUTTON + i ) );
return true;
}
return false;
}
bool IOKitJoystick::ProcessPOV( const IOHIDValueRef value )
{
const IOHIDElementRef element = IOHIDValueGetElement( value );
if ( NULL == element )
{
return false;
}
for ( size_t i = 0, count = m_POVs.Size(); i < count; ++i )
{
if ( element != m_POVs[i] )
{
continue;
}
const CFIndex direction = IOHIDValueGetIntegerValue( value );
// Default values is for Up/North
int oldButtons = 0;
int newButtons = 1;
int numButtons = 1;
int baseButton = KEY_JOYPOV1_UP;
switch ( direction )
{
case 0: // N
break;
case 1: // NE
newButtons = 3;
numButtons = 2;
break;
case 2: // E
baseButton = KEY_JOYPOV1_RIGHT;
break;
case 3: // SE
newButtons = 3;
numButtons = 2;
baseButton = KEY_JOYPOV1_RIGHT;
break;
case 4: // S
baseButton = KEY_JOYPOV1_DOWN;
break;
case 5: // SW
newButtons = 3;
numButtons = 2;
baseButton = KEY_JOYPOV1_DOWN;
break;
case 6: // W
baseButton = KEY_JOYPOV1_LEFT;
break;
case 7: // NW
newButtons = 9; // UP and LEFT
numButtons = 4;
break;
default:
// release all four directions
oldButtons = 15;
newButtons = 0;
numButtons = 4;
break;
}
Joy_GenerateButtonEvents( oldButtons, newButtons, numButtons,
static_cast< int >( baseButton + i * 4 ) );
}
return false;
}
// ---------------------------------------------------------------------------
class IOKitJoystickManager
{
public:
IOKitJoystickManager();
~IOKitJoystickManager();
void GetJoysticks( TArray< IJoystickConfig* >& joysticks ) const;
void AddAxes( float axes[ NUM_JOYAXIS ] ) const;
// Updates axes/buttons states
void Update();
// Rebuilds device list
void Rescan();
private:
TArray< IOKitJoystick* > m_joysticks;
static void OnDeviceChanged( void* context, IOReturn result, void* sender, IOHIDDeviceRef device );
void ReleaseJoysticks();
void EnableCallbacks();
void DisableCallbacks();
};
IOKitJoystickManager::IOKitJoystickManager()
{
Rescan();
}
IOKitJoystickManager::~IOKitJoystickManager()
{
ReleaseJoysticks();
DisableCallbacks();
HIDReleaseDeviceList();
}
void IOKitJoystickManager::GetJoysticks( TArray< IJoystickConfig* >& joysticks ) const
{
const size_t joystickCount = m_joysticks.Size();
joysticks.Resize( joystickCount );
for ( size_t i = 0; i < joystickCount; ++i )
{
M_LoadJoystickConfig( m_joysticks[i] );
joysticks[i] = m_joysticks[i];
}
}
void IOKitJoystickManager::AddAxes( float axes[ NUM_JOYAXIS ] ) const
{
for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i )
{
m_joysticks[i]->AddAxes( axes );
}
}
void IOKitJoystickManager::Update()
{
for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i )
{
m_joysticks[i]->Update();
}
}
void IOKitJoystickManager::Rescan()
{
ReleaseJoysticks();
DisableCallbacks();
const int usageCount = 2;
const UInt32 usagePages[ usageCount ] =
{
kHIDPage_GenericDesktop,
kHIDPage_GenericDesktop
};
const UInt32 usages[ usageCount ] =
{
kHIDUsage_GD_Joystick,
kHIDUsage_GD_GamePad
};
if ( HIDUpdateDeviceList( usagePages, usages, usageCount ) )
{
IOHIDDeviceRef device = HIDGetFirstDevice();
while ( NULL != device )
{
IOKitJoystick* joystick = new IOKitJoystick( device );
m_joysticks.Push( joystick );
device = HIDGetNextDevice( device );
}
}
else
{
Printf( "IOKitJoystickManager: Failed to build gamepad/joystick device list.\n" );
}
EnableCallbacks();
}
void IOKitJoystickManager::OnDeviceChanged( void* context, IOReturn result, void* sender, IOHIDDeviceRef device )
{
event_t event;
memset( &event, 0, sizeof( event ) );
event.type = EV_DeviceChange;
D_PostEvent( &event );
}
void IOKitJoystickManager::ReleaseJoysticks()
{
for ( size_t i = 0, count = m_joysticks.Size(); i < count; ++i )
{
delete m_joysticks[i];
}
m_joysticks.Clear();
}
void IOKitJoystickManager::EnableCallbacks()
{
if ( NULL == gIOHIDManagerRef )
{
return;
}
IOHIDManagerRegisterDeviceMatchingCallback( gIOHIDManagerRef, OnDeviceChanged, this );
IOHIDManagerRegisterDeviceRemovalCallback ( gIOHIDManagerRef, OnDeviceChanged, this );
IOHIDManagerScheduleWithRunLoop( gIOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
}
void IOKitJoystickManager::DisableCallbacks()
{
if ( NULL == gIOHIDManagerRef )
{
return;
}
IOHIDManagerUnscheduleFromRunLoop( gIOHIDManagerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );
IOHIDManagerRegisterDeviceMatchingCallback( gIOHIDManagerRef, NULL, NULL );
IOHIDManagerRegisterDeviceRemovalCallback ( gIOHIDManagerRef, NULL, NULL );
}
IOKitJoystickManager* s_joystickManager;
} // unnamed namespace
// ---------------------------------------------------------------------------
void I_StartupJoysticks()
{
// HID Manager API is available on 10.5 (Darwin 9.x) or newer
if (darwinVersion.major >= 9)
{
s_joystickManager = new IOKitJoystickManager;
}
}
void I_ShutdownJoysticks()
{
delete s_joystickManager;
}
void I_GetJoysticks( TArray< IJoystickConfig* >& sticks )
{
if ( NULL != s_joystickManager )
{
s_joystickManager->GetJoysticks( sticks );
}
}
void I_GetAxes( float axes[ NUM_JOYAXIS ] )
{
for ( size_t i = 0; i < NUM_JOYAXIS; ++i )
{
axes[i] = 0.0f;
}
if ( use_joystick && NULL != s_joystickManager )
{
s_joystickManager->AddAxes( axes );
}
}
IJoystickConfig* I_UpdateDeviceList()
{
if ( use_joystick && NULL != s_joystickManager )
{
s_joystickManager->Rescan();
}
return NULL;
}
// ---------------------------------------------------------------------------
void I_ProcessJoysticks()
{
if ( use_joystick && NULL != s_joystickManager )
{
s_joystickManager->Update();
}
}
#else // prior to 10.5
void I_StartupJoysticks()
{
}
void I_ShutdownJoysticks()
{
}
void I_GetJoysticks(TArray<IJoystickConfig*>& sticks)
{
sticks.Clear();
}
void I_GetAxes(float axes[NUM_JOYAXIS])
{
for (size_t i = 0; i < NUM_JOYAXIS; ++i)
{
axes[i] = 0.0f;
}
}
IJoystickConfig *I_UpdateDeviceList()
{
return NULL;
}
void I_ProcessJoysticks()
{
}
#endif // 10.5 or higher

43
src/cocoa/i_osversion.h Executable file
View File

@ -0,0 +1,43 @@
/*
** i_osversion.h
**
**---------------------------------------------------------------------------
** Copyright 2012-2014 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <stdint.h>
struct DarwinVersion
{
uint16_t major;
uint16_t minor;
uint16_t bugfix;
};
extern const DarwinVersion darwinVersion;

52
src/cocoa/i_rbopts.h Normal file
View File

@ -0,0 +1,52 @@
/*
** i_rbopts.h
**
**---------------------------------------------------------------------------
** Copyright 2014 Alexey Lysiuk
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#ifndef SRC_COCOA_I_RBOPTS_H_INCLUDED
#define SRC_COCOA_I_RBOPTS_H_INCLUDED
struct RenderBufferOptions
{
float pixelScale;
float shiftX;
float shiftY;
float width;
float height;
bool dirty;
};
extern RenderBufferOptions rbOpts;
#endif // SRC_COCOA_I_RBOPTS_H_INCLUDED

190
src/cocoa/i_timer.cpp Normal file
View File

@ -0,0 +1,190 @@
#include <assert.h>
#include <sys/time.h>
#include <pthread.h>
#include <libkern/OSAtomic.h>
#include <SDL.h>
#include "basictypes.h"
#include "basicinlines.h"
#include "doomdef.h"
#include "i_system.h"
#include "templates.h"
unsigned int I_MSTime()
{
return SDL_GetTicks();
}
unsigned int I_FPSTime()
{
return SDL_GetTicks();
}
bool g_isTicFrozen;
namespace
{
timespec GetNextTickTime()
{
static const long MILLISECONDS_IN_SECOND = 1000;
static const long MICROSECONDS_IN_SECOND = 1000 * MILLISECONDS_IN_SECOND;
static const long NANOSECONDS_IN_SECOND = 1000 * MICROSECONDS_IN_SECOND;
static timespec ts = {};
if (__builtin_expect((0 == ts.tv_sec), 0))
{
timeval tv;
gettimeofday(&tv, NULL);
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = (tv.tv_usec + MICROSECONDS_IN_SECOND / TICRATE) * MILLISECONDS_IN_SECOND;
}
else
{
ts.tv_nsec += (MICROSECONDS_IN_SECOND / TICRATE) * MILLISECONDS_IN_SECOND;
}
if (ts.tv_nsec >= NANOSECONDS_IN_SECOND)
{
ts.tv_sec++;
ts.tv_nsec -= NANOSECONDS_IN_SECOND;
}
return ts;
}
pthread_cond_t s_timerEvent;
pthread_mutex_t s_timerMutex;
pthread_t s_timerThread;
bool s_timerInitialized;
bool s_timerExitRequested;
uint32_t s_ticStart;
uint32_t s_timerStart;
int s_tics;
void* TimerThreadFunc(void*)
{
assert(s_timerInitialized);
assert(!s_timerExitRequested);
while (true)
{
if (s_timerExitRequested)
{
break;
}
const timespec timeToNextTick = GetNextTickTime();
pthread_mutex_lock(&s_timerMutex);
pthread_cond_timedwait(&s_timerEvent, &s_timerMutex, &timeToNextTick);
if (!g_isTicFrozen)
{
// The following GCC/Clang intrinsic can be used instead of OS X specific function:
// __sync_add_and_fetch(&s_tics, 1);
// Although it's not supported on all platform/compiler combination,
// e.g. GCC 4.0.1 with PowerPC target architecture
OSAtomicIncrement32(&s_tics);
}
s_timerStart = SDL_GetTicks();
pthread_cond_broadcast(&s_timerEvent);
pthread_mutex_unlock(&s_timerMutex);
}
return NULL;
}
int GetTimeThreaded(bool saveMS)
{
if (saveMS)
{
s_ticStart = s_timerStart;
}
return s_tics;
}
int WaitForTicThreaded(int prevTic)
{
assert(!g_isTicFrozen);
while (s_tics <= prevTic)
{
pthread_mutex_lock(&s_timerMutex);
pthread_cond_wait(&s_timerEvent, &s_timerMutex);
pthread_mutex_unlock(&s_timerMutex);
}
return s_tics;
}
void FreezeTimeThreaded(bool frozen)
{
g_isTicFrozen = frozen;
}
} // unnamed namespace
fixed_t I_GetTimeFrac(uint32* ms)
{
const uint32_t now = SDL_GetTicks();
if (NULL != ms)
{
*ms = s_ticStart + 1000 / TICRATE;
}
return 0 == s_ticStart
? FRACUNIT
: clamp<fixed_t>( (now - s_ticStart) * FRACUNIT * TICRATE / 1000, 0, FRACUNIT);
}
void I_InitTimer ()
{
assert(!s_timerInitialized);
s_timerInitialized = true;
pthread_cond_init (&s_timerEvent, NULL);
pthread_mutex_init(&s_timerMutex, NULL);
pthread_create(&s_timerThread, NULL, TimerThreadFunc, NULL);
I_GetTime = GetTimeThreaded;
I_WaitForTic = WaitForTicThreaded;
I_FreezeTime = FreezeTimeThreaded;
}
void I_ShutdownTimer ()
{
if (!s_timerInitialized)
{
// This might happen if Cancel button was pressed
// in the IWAD selector window
return;
}
s_timerExitRequested = true;
pthread_join(s_timerThread, NULL);
pthread_mutex_destroy(&s_timerMutex);
pthread_cond_destroy (&s_timerEvent);
}

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>zdoom.icns</string>
<key>CFBundleIdentifier</key>
<string>org.zdoom.zdoom</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>ZDoom</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>Version 2.8.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.action-games</string>
<key>LSMinimumSystemVersion</key>
<string>10.4</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Doom Resource File</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleTypeExtensions</key>
<array>
<string>wad</string>
<string>pk3</string>
<string>zip</string>
<string>pk7</string>
<string>7z</string>
</array>
</dict>
</array>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

BIN
src/cocoa/zdoom.icns Normal file

Binary file not shown.

View File

@ -81,6 +81,8 @@ enum
CP_SECTORFLOOROFFSET,
CP_SETWALLYSCALE,
CP_SETTHINGZ,
CP_SETTAG,
CP_SETTHINGFLAGS,
};
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@ -140,6 +142,7 @@ static FCompatOption Options[] =
{ "maskedmidtex", COMPATF_MASKEDMIDTEX, SLOT_COMPAT },
{ "badangles", COMPATF2_BADANGLES, SLOT_COMPAT2 },
{ "floormove", COMPATF2_FLOORMOVE, SLOT_COMPAT2 },
{ "soundcutoff", COMPATF2_SOUNDCUTOFF, SLOT_COMPAT2 },
{ NULL, 0, 0 }
};
@ -307,6 +310,24 @@ void ParseCompatibility()
sc.MustGetFloat();
CompatParams.Push(FLOAT2FIXED(sc.Float));
}
else if (sc.Compare("setsectortag"))
{
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SETTAG);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
}
else if (sc.Compare("setthingflags"))
{
if (flags.ExtCommandIndex == ~0u) flags.ExtCommandIndex = CompatParams.Size();
CompatParams.Push(CP_SETTHINGFLAGS);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
sc.MustGetNumber();
CompatParams.Push(sc.Number);
}
else
{
sc.UnGet();
@ -520,6 +541,24 @@ void SetCompatibilityParams()
i += 3;
break;
}
case CP_SETTAG:
{
if ((unsigned)CompatParams[i + 1] < (unsigned)numsectors)
{
sectors[CompatParams[i + 1]].tag = CompatParams[i + 2];
}
i += 3;
break;
}
case CP_SETTHINGFLAGS:
{
if ((unsigned)CompatParams[i + 1] < MapThingsConverted.Size())
{
MapThingsConverted[CompatParams[i + 1]].flags = CompatParams[i + 2];
}
i += 3;
break;
}
}
}
}

View File

@ -145,12 +145,20 @@ bool CT_Responder (event_t *ev)
CT_BackSpace ();
return true;
}
#ifdef __APPLE__
else if (ev->data1 == 'C' && (ev->data3 & GKM_META))
#else // !__APPLE__
else if (ev->data1 == 'C' && (ev->data3 & GKM_CTRL))
#endif // __APPLE__
{
I_PutInClipboard ((char *)ChatQueue);
return true;
}
#ifdef __APPLE__
else if (ev->data1 == 'V' && (ev->data3 & GKM_META))
#else // !__APPLE__
else if (ev->data1 == 'V' && (ev->data3 & GKM_CTRL))
#endif // __APPLE__
{
CT_PasteChat(I_GetFromClipboard(false));
}
@ -273,7 +281,8 @@ void CT_Drawer (void)
if (players[consoleplayer].camera != NULL &&
(Button_ShowScores.bDown ||
players[consoleplayer].camera->health <= 0) &&
players[consoleplayer].camera->health <= 0 ||
SB_ForceActive) &&
// Don't draw during intermission, since it has its own scoreboard in wi_stuff.cpp.
gamestate != GS_INTERMISSION)
{

View File

@ -2208,6 +2208,13 @@ static int PatchText (int oldSize)
{
strncpy (deh.PlayerSprite, newStr, 4);
}
for (unsigned ii = 0; ii < OrgSprNames.Size(); ii++)
{
if (!stricmp(OrgSprNames[ii].c, oldStr))
{
strcpy(OrgSprNames[ii].c, newStr);
}
}
// If this sprite is used by a pickup, then the DehackedPickup sprite map
// needs to be updated too.
for (i = 0; (size_t)i < countof(DehSpriteMappings); ++i)
@ -2294,7 +2301,10 @@ static int PatchStrings (int dummy)
ReplaceSpecialChars (holdstring.LockBuffer());
holdstring.UnlockBuffer();
GStrings.SetString (Line1, holdstring);
// Account for a discrepancy between Boom's and ZDoom's name for the red skull key pickup message
const char *ll = Line1;
if (!stricmp(ll, "GOTREDSKULL")) ll = "GOTREDSKUL";
GStrings.SetString (ll, holdstring);
DPrintf ("%s set to:\n%s\n", Line1, holdstring.GetChars());
}

View File

@ -61,6 +61,7 @@ typedef enum
ga_loadlevel,
ga_newgame,
ga_newgame2,
ga_recordgame,
ga_loadgame,
ga_loadgamehidecon,
ga_loadgameplaydemo,

View File

@ -70,7 +70,8 @@ enum GUIKeyModifiers
GKM_SHIFT = 1,
GKM_CTRL = 2,
GKM_ALT = 4,
GKM_LBUTTON = 8
GKM_META = 8,
GKM_LBUTTON = 16
};
// Special codes for some GUI keys, including a few real ASCII codes.

View File

@ -430,27 +430,11 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
}
}
}
#ifdef _WIN32
FString steam_path = I_GetSteamPath();
if (steam_path.IsNotEmpty())
TArray<FString> steam_path = I_GetSteamPath();
for (i = 0; i < steam_path.Size(); ++i)
{
static const char *const steam_dirs[] =
{
"doom 2/base",
"final doom/base",
"heretic shadow of the serpent riders/base",
"hexen/base",
"hexen deathkings of the dark citadel/base",
"ultimate doom/base",
"DOOM 3 BFG Edition/base/wads"
};
steam_path += "/SteamApps/common/";
for (i = 0; i < countof(steam_dirs); ++i)
{
CheckIWAD (steam_path + steam_dirs[i], &wads[0]);
}
CheckIWAD (steam_path[i], &wads[0]);
}
#endif
}
if (iwadparm != NULL && !wads[0].Path.IsEmpty())

View File

@ -466,7 +466,7 @@ CUSTOM_CVAR (Int, dmflags2, 0, CVAR_SERVERINFO)
}
// Come out of chasecam mode if we're not allowed to use chasecam.
if (!(dmflags2 & DF2_CHASECAM) && !G_SkillProperty (SKILLP_DisableCheats) && !sv_cheats)
if (!(dmflags2 & DF2_CHASECAM) && CheckCheatmode(false))
{
// Take us out of chasecam mode only.
if (p->cheats & CF_CHASECAM)
@ -620,6 +620,7 @@ CVAR (Flag, compat_polyobj, compatflags, COMPATF_POLYOBJ);
CVAR (Flag, compat_maskedmidtex, compatflags, COMPATF_MASKEDMIDTEX);
CVAR (Flag, compat_badangles, compatflags2, COMPATF2_BADANGLES);
CVAR (Flag, compat_floormove, compatflags2, COMPATF2_FLOORMOVE);
CVAR (Flag, compat_soundcutoff, compatflags2, COMPATF2_SOUNDCUTOFF);
//==========================================================================
//
@ -756,9 +757,9 @@ void D_Display ()
}
screen->SetBlendingRect(viewwindowx, viewwindowy,
viewwindowx + viewwidth, viewwindowy + viewheight);
P_PredictPlayer(&players[consoleplayer]);
Renderer->RenderView(&players[consoleplayer]);
P_UnPredictPlayer();
if ((hw2d = screen->Begin2D(viewactive)))
{
// Redraw everything every frame when using 2D accel
@ -832,15 +833,23 @@ void D_Display ()
}
}
// draw pause pic
if (paused && menuactive == MENU_Off)
if ((paused || pauseext) && menuactive == MENU_Off)
{
FTexture *tex;
int x;
FString pstring = "By ";
tex = TexMan(gameinfo.PauseSign);
x = (SCREENWIDTH - tex->GetScaledWidth() * CleanXfac)/2 +
tex->GetScaledLeftOffset() * CleanXfac;
screen->DrawTexture (tex, x, 4, DTA_CleanNoMove, true, TAG_DONE);
if (paused && multiplayer)
{
pstring += players[paused - 1].userinfo.GetName();
screen->DrawText(SmallFont, CR_RED,
(screen->GetWidth() - SmallFont->StringWidth(pstring)*CleanXfac) / 2,
(tex->GetScaledHeight() * CleanYfac) + 4, pstring, DTA_CleanNoMove, true, TAG_DONE);
}
}
// [RH] Draw icon, if any
@ -974,25 +983,6 @@ void D_DoomLoop ()
I_StartTic ();
D_ProcessEvents ();
G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]);
//Added by MC: For some of that bot stuff. The main bot function.
int i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].isbot && players[i].mo)
{
players[i].savedyaw = players[i].mo->angle;
players[i].savedpitch = players[i].mo->pitch;
}
}
bglobal.Main (maketic%BACKUPTICS);
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].isbot && players[i].mo)
{
players[i].mo->angle = players[i].savedyaw;
players[i].mo->pitch = players[i].savedpitch;
}
}
if (advancedemo)
D_DoAdvanceDemo ();
C_Ticker ();
@ -1337,6 +1327,7 @@ CCMD (endgame)
{
gameaction = ga_fullconsole;
demosequence = -1;
G_CheckDemoStatus();
}
}
@ -2588,6 +2579,7 @@ void D_DoomMain (void)
new (&gameinfo) gameinfo_t; // Reset gameinfo
S_Shutdown(); // free all channels and delete playlist
C_ClearAliases(); // CCMDs won't be reinitialized so these need to be deleted here
DestroyCVarsFlagged(CVAR_MOD); // Delete any cvar left by mods
GC::FullGC(); // perform one final garbage collection before deleting the class data
restart++;

View File

@ -39,7 +39,6 @@
#include "cmdlib.h"
#include "s_sound.h"
#include "m_cheat.h"
#include "p_effect.h"
#include "p_local.h"
#include "c_dispatch.h"
#include "sbar.h"
@ -110,13 +109,15 @@ unsigned int lastrecvtime[MAXPLAYERS]; // [RH] Used for pings
unsigned int currrecvtime[MAXPLAYERS];
unsigned int lastglobalrecvtime; // Identify the last time a packet was recieved.
bool hadlate;
int netdelay[MAXNETNODES][BACKUPTICS]; // Used for storing network delay times.
int lastaverage;
int nodeforplayer[MAXPLAYERS];
int playerfornode[MAXNETNODES];
int maketic;
int skiptics;
int ticdup;
int ticdup;
void D_ProcessEvents (void);
void G_BuildTiccmd (ticcmd_t *cmd);
@ -151,6 +152,32 @@ CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
}
}
CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO | CVAR_NOSAVE)
CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO | CVAR_NOSAVE)
{
if (self < 0)
{
self = 0;
}
else if (self > 2)
{
self = 2;
}
}
#ifdef _DEBUG
CVAR(Int, net_fakelatency, 0, 0);
struct PacketStore
{
int timer;
doomcom_t message;
};
static TArray<PacketStore> InBuffer;
static TArray<PacketStore> OutBuffer;
#endif
// [RH] Special "ticcmds" get stored in here
static struct TicSpecial
{
@ -347,6 +374,9 @@ int NetbufferSize ()
k += netbuffer[k] + 1;
}
// Network delay byte
k++;
if (netbuffer[0] & NCMD_MULTI)
{
count = netbuffer[k];
@ -487,7 +517,30 @@ void HSendPacket (int node, int len)
doomcom.remotenode = node;
doomcom.datalength = len;
I_NetCmd ();
#ifdef _DEBUG
if (net_fakelatency / 2 > 0)
{
PacketStore store;
store.message = doomcom;
store.timer = I_GetTime(false) + ((net_fakelatency / 2) / (1000 / TICRATE));
OutBuffer.Push(store);
}
else
I_NetCmd();
for (unsigned int i = 0; i < OutBuffer.Size(); i++)
{
if (OutBuffer[i].timer <= I_GetTime(false))
{
doomcom = OutBuffer[i].message;
I_NetCmd();
OutBuffer.Delete(i);
i = -1;
}
}
#else
I_NetCmd();
#endif
}
//
@ -509,12 +562,42 @@ bool HGetPacket (void)
if (demoplayback)
return false;
doomcom.command = CMD_GET;
I_NetCmd ();
#ifdef _DEBUG
if (net_fakelatency / 2 > 0 && doomcom.remotenode != -1)
{
PacketStore store;
store.message = doomcom;
store.timer = I_GetTime(false) + ((net_fakelatency / 2) / (1000 / TICRATE));
InBuffer.Push(store);
doomcom.remotenode = -1;
}
if (doomcom.remotenode == -1)
{
bool gotmessage = false;
for (unsigned int i = 0; i < InBuffer.Size(); i++)
{
if (InBuffer[i].timer <= I_GetTime(false))
{
doomcom = InBuffer[i].message;
InBuffer.Delete(i);
gotmessage = true;
break;
}
}
if (!gotmessage)
return false;
}
#else
if (doomcom.remotenode == -1)
{
return false;
}
#endif
if (debugfile)
{
@ -570,6 +653,9 @@ bool HGetPacket (void)
if (doomcom.datalength != NetbufferSize ())
{
Printf("Bad packet length %i (calculated %i)\n",
doomcom.datalength, NetbufferSize());
if (debugfile)
fprintf (debugfile,"---bad packet length %i (calculated %i)\n",
doomcom.datalength, NetbufferSize());
@ -583,6 +669,9 @@ void PlayerIsGone (int netnode, int netconsole)
{
int i;
if (!nodeingame[netnode])
return;
for (i = netnode + 1; i < doomcom.numnodes; ++i)
{
if (nodeingame[i])
@ -593,77 +682,37 @@ void PlayerIsGone (int netnode, int netconsole)
doomcom.numnodes = netnode;
}
if (playeringame[netconsole])
{
players[netconsole].playerstate = PST_GONE;
}
nodeingame[netnode] = false;
playeringame[netconsole] = false;
nodejustleft[netnode] = false;
if (deathmatch)
{
Printf ("%s left the game with %d frags\n",
players[netconsole].userinfo.GetName(),
players[netconsole].fragcount);
}
else
{
Printf ("%s left the game\n", players[netconsole].userinfo.GetName());
}
// [RH] Revert each player to their own view if spying through the player who left
for (int ii = 0; ii < MAXPLAYERS; ++ii)
{
if (playeringame[ii] && players[ii].camera == players[netconsole].mo)
{
players[ii].camera = players[ii].mo;
if (ii == consoleplayer && StatusBar != NULL)
{
StatusBar->AttachToPlayer (&players[ii]);
}
}
}
// [RH] Make the player disappear
FBehavior::StaticStopMyScripts (players[netconsole].mo);
if (players[netconsole].mo != NULL)
{
P_DisconnectEffect (players[netconsole].mo);
players[netconsole].mo->player = NULL;
players[netconsole].mo->Destroy ();
if (!(players[netconsole].mo->ObjectFlags & OF_EuthanizeMe))
{ // We just destroyed a morphed player, so now the original player
// has taken their place. Destroy that one too.
players[netconsole].mo->Destroy();
}
players[netconsole].mo = NULL;
players[netconsole].camera = NULL;
}
// [RH] Let the scripts know the player left
FBehavior::StaticStartTypedScripts (SCRIPT_Disconnect, NULL, true, netconsole);
if (netconsole == Net_Arbitrator)
{
bglobal.RemoveAllBots (true);
Printf ("Removed all bots\n");
// Pick a new network arbitrator
for (int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && !players[i].isbot)
if (i != netconsole && playeringame[i] && players[i].Bot == NULL)
{
Net_Arbitrator = i;
players[i].settings_controller = true;
Printf ("%s is the new arbitrator\n", players[i].userinfo.GetName());
Printf("%s is the new arbitrator\n", players[i].userinfo.GetName());
break;
}
}
if (debugfile && NetMode == NET_PacketServer)
}
if (debugfile && NetMode == NET_PacketServer)
{
if (Net_Arbitrator == consoleplayer)
{
if (Net_Arbitrator == consoleplayer)
{
fprintf (debugfile, "I am the new master!\n");
}
else
{
fprintf (debugfile, "Node %d is the new master!\n", nodeforplayer[Net_Arbitrator]);
}
fprintf(debugfile, "I am the new master!\n");
}
else
{
fprintf(debugfile, "Node %d is the new master!\n", nodeforplayer[Net_Arbitrator]);
}
}
@ -772,7 +821,6 @@ void GetPackets (void)
}
if (netbuffer[0] & NCMD_QUITTERS)
{
numplayers = netbuffer[k++];
for (int i = 0; i < numplayers; ++i)
@ -782,6 +830,9 @@ void GetPackets (void)
}
}
// Pull current network delay from node
netdelay[netnode][(nettics[netnode]+1) % BACKUPTICS] = netbuffer[k++];
playerbytes[0] = netconsole;
if (netbuffer[0] & NCMD_MULTI)
{
@ -847,64 +898,18 @@ void GetPackets (void)
for (i = 0; i < numplayers; ++i)
{
int node = !players[playerbytes[i]].isbot ?
nodeforplayer[playerbytes[i]] : netnode;
int node = nodeforplayer[playerbytes[i]];
SkipTicCmd (&start, nettics[node] - realstart);
for (tics = nettics[node]; tics < realend; tics++)
ReadTicCmd (&start, playerbytes[i], tics);
}
// Update the number of tics received from each node. This must
// be separate from the above loop in case the master is also
// sending bot movements. If it's not separate, then the bots
// will only move on the master, because the other players will
// read the master's tics and then think they already got all
// the tics for the bots and skip the bot tics included in the
// packet.
for (i = 0; i < numplayers; ++i)
{
if (!players[playerbytes[i]].isbot)
{
nettics[nodeforplayer[playerbytes[i]]] = realend;
}
}
}
}
}
void AdjustBots (int gameticdiv)
{
// [RH] This loop adjusts the bots' rotations for ticcmds that have
// been already created but not yet executed. This way, the bot is still
// able to create ticcmds that accurately reflect the state it wants to
// be in even when gametic lags behind maketic.
for (int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].isbot && players[i].mo)
{
players[i].savedyaw = players[i].mo->angle;
players[i].savedpitch = players[i].mo->pitch;
for (int j = gameticdiv; j < maketic/ticdup; j++)
{
players[i].mo->angle += (netcmds[i][j%BACKUPTICS].ucmd.yaw << 16) * ticdup;
players[i].mo->pitch -= (netcmds[i][j%BACKUPTICS].ucmd.pitch << 16) * ticdup;
nettics[nodeforplayer[playerbytes[i]]] = realend;
}
}
}
}
void UnadjustBots ()
{
for (int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].isbot && players[i].mo)
{
players[i].mo->angle = players[i].savedyaw;
players[i].mo->pitch = players[i].savedpitch;
}
}
}
//
// NetUpdate
// Builds ticcmds for console player,
@ -934,7 +939,7 @@ void NetUpdate (void)
newtics = nowtime - gametime;
gametime = nowtime;
if (newtics <= 0) // nothing new to update
if (newtics <= 0 || pauseext) // nothing new to update or window paused
{
GetPackets ();
return;
@ -951,9 +956,7 @@ void NetUpdate (void)
newtics = 0;
}
// build new ticcmds for console player (and bots if I am the arbitrator)
AdjustBots (gametic / ticdup);
// build new ticcmds for console player
for (i = 0; i < newtics; i++)
{
I_StartTic ();
@ -963,11 +966,6 @@ void NetUpdate (void)
//Printf ("mk:%i ",maketic);
G_BuildTiccmd (&localcmds[maketic % LOCALCMDTICS]);
if (maketic % ticdup == 0)
{
//Added by MC: For some of that bot stuff. The main bot function.
bglobal.Main ((maketic / ticdup) % BACKUPTICS);
}
maketic++;
if (ticdup == 1 || maketic == 0)
@ -1047,8 +1045,6 @@ void NetUpdate (void)
}
}
UnadjustBots ();
if (singletics)
return; // singletic update is synchronous
@ -1068,14 +1064,11 @@ void NetUpdate (void)
if (consoleplayer == Net_Arbitrator)
{
for (j = 0; j < MAXPLAYERS; j++)
for (j = 0; j < doomcom.numnodes; j++)
{
if (playeringame[j])
if (nodeingame[j] && NetMode == NET_PacketServer)
{
if (players[j].isbot || NetMode == NET_PacketServer)
{
count++;
}
count++;
}
}
@ -1150,11 +1143,18 @@ void NetUpdate (void)
netbuffer[k++] = lowtic;
}
numtics = lowtic - realstart;
numtics = MAX(0, lowtic - realstart);
if (numtics > BACKUPTICS)
I_Error ("NetUpdate: Node %d missed too many tics", i);
resendto[i] = MAX (0, lowtic - doomcom.extratics);
switch (net_extratic)
{
case 0:
default:
resendto[i] = lowtic; break;
case 1: resendto[i] = MAX(0, lowtic - 1); break;
case 2: resendto[i] = nettics[i]; break;
}
if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i])
{
@ -1190,6 +1190,10 @@ void NetUpdate (void)
}
}
// Send current network delay
// The number of tics we just made should be removed from the count.
netbuffer[k++] = ((maketic - newtics - gametic) / ticdup);
if (numtics > 0)
{
int l;
@ -1199,15 +1203,12 @@ void NetUpdate (void)
netbuffer[0] |= NCMD_MULTI;
netbuffer[k++] = count;
for (l = 1, j = 0; j < MAXPLAYERS; j++)
for (l = 1, j = 0; j < doomcom.numnodes; j++)
{
if (playeringame[j] && j != playerfornode[i] && j != consoleplayer)
if (nodeingame[j] && j != i && j != nodeforplayer[consoleplayer] && NetMode == NET_PacketServer)
{
if (players[j].isbot || NetMode == NET_PacketServer)
{
playerbytes[l++] = j;
netbuffer[k++] = j;
}
playerbytes[l++] = playerfornode[j];
netbuffer[k++] = playerfornode[j];
}
}
}
@ -1227,7 +1228,7 @@ void NetUpdate (void)
prev %= BACKUPTICS;
// The local player has their tics sent first, followed by
// the other players/bots.
// the other players.
if (l == 0)
{
WriteWord (localcmds[localstart].consistancy, &cmddata);
@ -1242,24 +1243,17 @@ void NetUpdate (void)
}
else if (i != 0)
{
if (players[playerbytes[l]].isbot)
{
int len;
BYTE *spec;
WriteWord (0, &cmddata); // fake consistancy word
}
else
WriteWord (netcmds[playerbytes[l]][start].consistancy, &cmddata);
spec = NetSpecs[playerbytes[l]][start].GetData (&len);
if (spec != NULL)
{
int len;
BYTE *spec;
WriteWord (netcmds[playerbytes[l]][start].consistancy, &cmddata);
spec = NetSpecs[playerbytes[l]][start].GetData (&len);
if (spec != NULL)
{
memcpy (cmddata, spec, len);
cmddata += len;
}
memcpy (cmddata, spec, len);
cmddata += len;
}
WriteUserCmdMessage (&netcmds[playerbytes[l]][start].ucmd,
prev >= 0 ? &netcmds[playerbytes[l]][prev].ucmd : NULL, &cmddata);
}
@ -1299,9 +1293,37 @@ void NetUpdate (void)
// that it won't adapt. Fortunately, player prediction helps
// alleviate the lag somewhat.
if (NetMode != NET_PacketServer)
if (NetMode == NET_PeerToPeer)
{
mastertics = nettics[nodeforplayer[Net_Arbitrator]];
int totalavg = 0;
if (net_ticbalance)
{
// Try to guess ahead the time it takes to send responses to the slowest node
int nodeavg = 0, arbavg = 0;
for (j = 0; j < BACKUPTICS; j++)
{
arbavg += netdelay[nodeforplayer[Net_Arbitrator]][j];
nodeavg += netdelay[0][j];
}
arbavg /= BACKUPTICS;
nodeavg /= BACKUPTICS;
// We shouldn't adapt if we are already the arbitrator isn't what we are waiting for, otherwise it just adds more latency
if (arbavg > nodeavg)
{
lastaverage = totalavg = ((arbavg + nodeavg) / 2);
}
else
{
// Allow room to guess two tics ahead
if (nodeavg > (arbavg + 2) && lastaverage > 0)
lastaverage--;
totalavg = lastaverage;
}
}
mastertics = nettics[nodeforplayer[Net_Arbitrator]] + totalavg;
}
if (nettics[0] <= mastertics)
{
@ -1346,9 +1368,8 @@ void NetUpdate (void)
//
// 0 One byte set to NCMD_SETUP+2
// 1 One byte for ticdup setting
// 2 One byte for extratics setting
// 3 One byte for NetMode setting
// 4 String with starting map's name
// 2 One byte for NetMode setting
// 3 String with starting map's name
// . Four bytes for the RNG seed
// . Stream containing remaining game info
//
@ -1429,10 +1450,9 @@ bool DoArbitrate (void *userdata)
data->gotsetup[0] = 0x80;
ticdup = doomcom.ticdup = netbuffer[1];
doomcom.extratics = netbuffer[2];
NetMode = netbuffer[3];
NetMode = netbuffer[2];
stream = &netbuffer[4];
stream = &netbuffer[3];
s = ReadString (&stream);
startmap = s;
delete[] s;
@ -1497,9 +1517,8 @@ bool DoArbitrate (void *userdata)
{
netbuffer[0] = NCMD_SETUP+2;
netbuffer[1] = (BYTE)doomcom.ticdup;
netbuffer[2] = (BYTE)doomcom.extratics;
netbuffer[3] = NetMode;
stream = &netbuffer[4];
netbuffer[2] = NetMode;
stream = &netbuffer[3];
WriteString (startmap, &stream);
WriteLong (rngseed, &stream);
C_WriteCVars (&stream, CVAR_SERVERINFO, true);
@ -1634,10 +1653,19 @@ void D_CheckNetGame (void)
resendto[i] = 0; // which tic to start sending
}
// Packet server has proven to be rather slow over the internet. Print a warning about it.
v = Args->CheckValue("-netmode");
if (v != NULL && (atoi(v) != 0))
{
Printf(TEXTCOLOR_YELLOW "Notice: Using PacketServer (netmode 1) over the internet is prone to running too slow on some internet configurations."
"\nIf the game is running well below expected speeds, use netmode 0 (P2P) instead.\n");
}
// I_InitNetwork sets doomcom and netgame
if (I_InitNetwork ())
{
NetMode = NET_PacketServer;
// For now, stop auto selecting PacketServer, as it's more likely to cause confusion.
//NetMode = NET_PacketServer;
}
if (doomcom.id != DOOMCOM_ID)
{
@ -1647,15 +1675,23 @@ void D_CheckNetGame (void)
consoleplayer = doomcom.consoleplayer;
v = Args->CheckValue ("-netmode");
if (v != NULL)
if (consoleplayer == Net_Arbitrator)
{
NetMode = atoi (v) != 0 ? NET_PacketServer : NET_PeerToPeer;
}
if (doomcom.numnodes > 1)
{
Printf ("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server",
v != NULL ? "forced" : "auto");
v = Args->CheckValue("-netmode");
if (v != NULL)
{
NetMode = atoi(v) != 0 ? NET_PacketServer : NET_PeerToPeer;
}
if (doomcom.numnodes > 1)
{
Printf("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server",
v != NULL ? "forced" : "auto");
}
if (Args->CheckParm("-extratic"))
{
net_extratic = 1;
}
}
// [RH] Setup user info
@ -1683,6 +1719,11 @@ void D_CheckNetGame (void)
for (i = 0; i < doomcom.numnodes; i++)
nodeingame[i] = true;
if (consoleplayer != Net_Arbitrator && doomcom.numnodes > 1)
{
Printf("Arbitrator selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode.\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server");
}
Printf ("player %i of %i (%i nodes)\n",
consoleplayer+1, doomcom.numplayers, doomcom.numnodes);
}
@ -1809,6 +1850,9 @@ void TryRunTics (void)
{
C_Ticker();
M_Ticker();
// Repredict the player for new buffered movement
P_UnPredictPlayer();
P_PredictPlayer(&players[consoleplayer]);
}
return;
}
@ -1844,6 +1888,9 @@ void TryRunTics (void)
{
C_Ticker ();
M_Ticker ();
// Repredict the player for new buffered movement
P_UnPredictPlayer();
P_PredictPlayer(&players[consoleplayer]);
return;
}
}
@ -1857,6 +1904,7 @@ void TryRunTics (void)
// run the count tics
if (counts > 0)
{
P_UnPredictPlayer();
while (counts--)
{
if (gametic > lowtic)
@ -1876,6 +1924,7 @@ void TryRunTics (void)
NetUpdate (); // check for new console commands
}
P_PredictPlayer(&players[consoleplayer]);
S_UpdateSounds (players[consoleplayer].camera); // move positional sounds
}
}
@ -2130,10 +2179,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
break;
case DEM_ADDBOT:
{
BYTE num = ReadByte (stream);
bglobal.DoAddBot (num, s = ReadString (stream));
}
bglobal.TryAddBot (stream, player);
break;
case DEM_KILLBOTS:
@ -2589,10 +2635,13 @@ void Net_SkipCommand (int type, BYTE **stream)
switch (type)
{
case DEM_SAY:
case DEM_ADDBOT:
skip = strlen ((char *)(*stream + 1)) + 2;
break;
case DEM_ADDBOT:
skip = strlen ((char *)(*stream + 1)) + 6;
break;
case DEM_GIVECHEAT:
case DEM_TAKECHEAT:
skip = strlen ((char *)(*stream)) + 3;
@ -2713,7 +2762,6 @@ void Net_SkipCommand (int type, BYTE **stream)
CCMD (pings)
{
int i;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
Printf ("% 4d %s\n", currrecvtime[i] - lastrecvtime[i],
@ -2755,7 +2803,7 @@ static void Network_Controller (int playernum, bool add)
return;
}
if (players[playernum].isbot)
if (players[playernum].Bot != NULL)
{
Printf ("Bots cannot be added to the controller list.\n");
return;

View File

@ -70,7 +70,6 @@ struct doomcom_t
// info common to all nodes
SWORD numnodes; // console is always node 0.
SWORD ticdup; // 1 = no duplication, 2-5 = dup for slow nets
SWORD extratics; // 1 = send a backup tic in every packet
#ifdef DJGPP
SWORD pad[5]; // keep things aligned for DOS drivers
#endif
@ -143,6 +142,8 @@ extern struct ticcmd_t localcmds[LOCALCMDTICS];
extern int maketic;
extern int nettics[MAXNETNODES];
extern int netdelay[MAXNETNODES][BACKUPTICS];
extern int nodeforplayer[MAXPLAYERS];
extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
extern int ticdup;

View File

@ -60,9 +60,6 @@
static FRandom pr_pickteam ("PickRandomTeam");
extern bool st_firsttime;
EXTERN_CVAR (Bool, teamplay)
CVAR (Float, autoaim, 5000.f, CVAR_USERINFO | CVAR_ARCHIVE);
CVAR (String, name, "Player", CVAR_USERINFO | CVAR_ARCHIVE);
CVAR (Color, color, 0x40cf00, CVAR_USERINFO | CVAR_ARCHIVE);

View File

@ -96,8 +96,6 @@ public:
};
FString GetPrintableDisplayName(PClassPlayerPawn *cls);
class player_t;
class APlayerPawn : public AActor
{
DECLARE_CLASS_WITH_META(APlayerPawn, AActor, PClassPlayerPawn)
@ -197,7 +195,8 @@ typedef enum
PST_LIVE, // Playing or camping.
PST_DEAD, // Dead on the ground, view follows killer.
PST_REBORN, // Ready to restart/respawn???
PST_ENTER // [BC] Entered the game
PST_ENTER, // [BC] Entered the game
PST_GONE // Player has left the game
} playerstate_t;
@ -227,6 +226,8 @@ typedef enum
CF_DOUBLEFIRINGSPEED= 1 << 21, // Player owns a double firing speed artifact
CF_EXTREMELYDEAD = 1 << 22, // [RH] Reliably let the status bar know about extreme deaths.
CF_INFINITEAMMO = 1 << 23, // Player owns an infinite ammo artifact
CF_BUDDHA2 = 1 << 24, // [MC] Absolute buddha. No voodoo can kill it either.
CF_GODMODE2 = 1 << 25, // [MC] Absolute godmode. No voodoo can kill it either.
CF_BUDDHA = 1 << 27, // [SP] Buddha mode - take damage, but don't die
CF_NOCLIP2 = 1 << 30, // [RH] More Quake-like noclip
} cheat_t;
@ -419,8 +420,7 @@ public:
int inventorytics;
BYTE CurrentPlayerClass; // class # for this player instance
bool backpack;
int frags[MAXPLAYERS]; // kills of other players
int fragcount; // [RH] Cumulative frags for this player
int lastkilltime; // [RH] For multikills
@ -465,47 +465,10 @@ public:
FName LastDamageType; // [RH] For damage-specific pain and death sounds
//Added by MC:
angle_t savedyaw;
int savedpitch;
angle_t angle; // The wanted angle that the bot try to get every tic.
// (used to get a smoth view movement)
TObjPtr<AActor> dest; // Move Destination.
TObjPtr<AActor> prev; // Previous move destination.
TObjPtr<AActor> enemy; // The dead meat.
TObjPtr<AActor> missile; // A threatening missile that needs to be avoided.
TObjPtr<AActor> mate; // Friend (used for grouping in teamplay or coop).
TObjPtr<AActor> last_mate; // If bots mate disappeared (not if died) that mate is
// pointed to by this. Allows bot to roam to it if
// necessary.
TObjPtr<DBot> Bot;
bool settings_controller; // Player can control game settings.
//Skills
struct botskill_t skill;
//Tickers
int t_active; // Open door, lower lift stuff, door must open and
// lift must go down before bot does anything
// radical like try a stuckmove
int t_respawn;
int t_strafe;
int t_react;
int t_fight;
int t_roam;
int t_rocket;
//Misc booleans
bool isbot;
bool first_shot; // Used for reaction skill.
bool sleft; // If false, strafe is right.
bool allround;
fixed_t oldx;
fixed_t oldy;
float BlendR; // [RH] Final blending values
float BlendG;
float BlendB;

View File

@ -112,7 +112,7 @@ enum EDemoCommand
DEM_DROPPLAYER, // 13 Not implemented, takes a byte
DEM_CHANGEMAP, // 14 Name of map to change to
DEM_SUICIDE, // 15 Player wants to die
DEM_ADDBOT, // 16 Byte: player#, String: userinfo for bot
DEM_ADDBOT, // 16 Byte: botshift, String: userinfo for bot, 4 Bytes: skill (aiming, perfection, reaction, isp)
DEM_KILLBOTS, // 17 Remove all bots from the world
DEM_INVUSEALL, // 18 Use every item (panic!)
DEM_INVUSE, // 19 4 bytes: ID of item to use
@ -219,7 +219,9 @@ enum ECheatCommand
CHT_GIMMIEJ,
CHT_GIMMIEZ,
CHT_BUDDHA,
CHT_NOCLIP2
CHT_NOCLIP2,
CHT_BUDDHA2,
CHT_GOD2
};
void StartChunk (int id, BYTE **stream);

View File

@ -162,6 +162,7 @@ enum ELineFlags
ML_BLOCKUSE = 0x02000000, // blocks all use actions through this line
ML_BLOCKSIGHT = 0x04000000, // blocks monster line of sight
ML_BLOCKHITSCAN = 0x08000000, // blocks hitscan attacks
ML_3DMIDTEX_IMPASS = 0x10000000, // [TP] if 3D midtex, behaves like a height-restricted ML_BLOCKING
};

View File

@ -339,6 +339,7 @@ enum
COMPATF2_BADANGLES = 1 << 0, // It is impossible to face directly NSEW.
COMPATF2_FLOORMOVE = 1 << 1, // Use the same floor motion behavior as Doom.
COMPATF2_SOUNDCUTOFF = 1 << 2, // Cut off sounds when an actor vanishes instead of making it owner-less
};
// Emulate old bugs for select maps. These are not exposed by a cvar

View File

@ -109,6 +109,7 @@ enum EMenuState
extern bool automapactive; // In AutoMap mode?
extern EMenuState menuactive; // Menu overlayed?
extern int paused; // Game Pause?
extern bool pauseext;
extern bool viewactive;
@ -136,6 +137,8 @@ extern int consoleplayer;
// Disable save/end game?
extern bool usergame;
extern FString newdemoname;
extern FString newdemomap;
extern bool demoplayback;
extern bool demorecording;
extern int demover;

View File

@ -113,6 +113,9 @@ enum SAW_Flags
SF_RANDOMLIGHTHIT = 4,
SF_NOUSEAMMOMISS = 8,
SF_NOUSEAMMO = 16,
SF_NOPULLIN = 32,
SF_NOTURN = 64,
SF_STEALARMOR = 128,
};
DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw)
@ -127,6 +130,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw)
PARAM_ANGLE_OPT (spread_xy) { spread_xy = angle_t(2.8125 * (ANGLE_90 / 90.0)); }
PARAM_ANGLE_OPT (spread_z) { spread_z = 0; }
PARAM_FIXED_OPT (lifesteal) { lifesteal = 0; }
PARAM_INT_OPT (lifestealmax) { lifestealmax = 0; }
PARAM_CLASS_OPT (armorbonustype, ABasicArmorBonus) { armorbonustype = NULL; }
angle_t angle;
angle_t slope;
@ -197,29 +202,58 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Saw)
if (lifesteal && !(linetarget->flags5 & MF5_DONTDRAIN))
{
P_GiveBody (self, (actualdamage * lifesteal) >> FRACBITS);
if (flags & SF_STEALARMOR)
{
if (armorbonustype == NULL)
{
armorbonustype = dyn_cast<ABasicArmorBonus::MetaClass>(PClass::FindClass("ArmorBonus"));
}
if (armorbonustype != NULL)
{
assert(armorbonustype->IsDescendantOf (RUNTIME_CLASS(ABasicArmorBonus)));
ABasicArmorBonus *armorbonus = static_cast<ABasicArmorBonus *>(Spawn(armorbonustype, 0,0,0, NO_REPLACE));
armorbonus->SaveAmount *= (actualdamage * lifesteal) >> FRACBITS;
armorbonus->MaxSaveAmount = lifestealmax <= 0 ? armorbonus->MaxSaveAmount : lifestealmax;
armorbonus->flags |= MF_DROPPED;
armorbonus->ClearCounters();
if (!armorbonus->CallTryPickup (self))
{
armorbonus->Destroy ();
}
}
}
else
{
P_GiveBody (self, (actualdamage * lifesteal) >> FRACBITS, lifestealmax);
}
}
S_Sound (self, CHAN_WEAPON, hitsound, 1, ATTN_NORM);
// turn to face target
angle = R_PointToAngle2 (self->x, self->y,
linetarget->x, linetarget->y);
if (angle - self->angle > ANG180)
if (!(flags & SF_NOTURN))
{
if (angle - self->angle < (angle_t)(-ANG90/20))
self->angle = angle + ANG90/21;
angle = R_PointToAngle2(self->x, self->y,
linetarget->x, linetarget->y);
if (angle - self->angle > ANG180)
{
if (angle - self->angle < (angle_t)(-ANG90 / 20))
self->angle = angle + ANG90 / 21;
else
self->angle -= ANG90 / 20;
}
else
self->angle -= ANG90/20;
{
if (angle - self->angle > ANG90 / 20)
self->angle = angle - ANG90 / 21;
else
self->angle += ANG90 / 20;
}
}
else
{
if (angle - self->angle > ANG90/20)
self->angle = angle - ANG90/21;
else
self->angle += ANG90/20;
}
self->flags |= MF_JUSTATTACKED;
if (!(flags & SF_NOPULLIN))
self->flags |= MF_JUSTATTACKED;
return 0;
}
@ -593,6 +627,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FireBFG)
return 0;
}
//
// A_BFGSpray
// Spawn a BFG explosion on every monster in view
@ -603,6 +638,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray)
PARAM_CLASS_OPT (spraytype, AActor) { spraytype = NULL; }
PARAM_INT_OPT (numrays) { numrays = 40; }
PARAM_INT_OPT (damagecnt) { damagecnt = 15; }
PARAM_ANGLE_OPT (angle) { angle = ANGLE_90; }
PARAM_FIXED_OPT (distance) { distance = 16*64*FRACUNIT; }
PARAM_ANGLE_OPT (vrange) { vrange = 32*FRACUNIT; }
PARAM_INT_OPT (defdamage) { defdamage = 0; }
int i;
int j;
@ -611,10 +650,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray)
AActor *thingToHit;
AActor *linetarget;
if (spraytype == NULL) spraytype = PClass::FindActor("BFGExtra");
if (numrays <= 0) numrays = 40;
if (damagecnt <= 0) damagecnt = 15;
if (angle == 0) angle = ANG90;
if (distance <= 0) distance = 16 * 64 * FRACUNIT;
if (vrange == 0) vrange = ANGLE_1 * 32;
// [RH] Don't crash if no target
if (!self->target)
@ -623,10 +664,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray)
// offset angles from its attack angle
for (i = 0; i < numrays; i++)
{
an = self->angle - ANG90/2 + ANG90/numrays*i;
an = self->angle - angle/2 + angle/numrays*i;
// self->target is the originator (player) of the missile
P_AimLineAttack (self->target, an, 16*64*FRACUNIT, &linetarget, ANGLE_1*32);
P_AimLineAttack (self->target, an, distance, &linetarget, vrange);
if (!linetarget)
continue;
@ -637,14 +678,24 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BFGSpray)
if (spray && (spray->flags5 & MF5_PUFFGETSOWNER))
spray->target = self->target;
damage = 0;
for (j = 0; j < damagecnt; ++j)
damage += (pr_bfgspray() & 7) + 1;
if (defdamage == 0)
{
damage = 0;
for (j = 0; j < damagecnt; ++j)
damage += (pr_bfgspray() & 7) + 1;
}
else
{
// if this is used, damagecnt will be ignored
damage = defdamage;
}
int dmgFlagPass = 0;
dmgFlagPass += (spray != NULL && (spray->flags3 & MF3_FOILINVUL)) ? DMG_FOILINVUL : 0; //[MC]Because the original foilinvul wasn't working.
dmgFlagPass += (spray != NULL && (spray->flags7 & MF7_FOILBUDDHA)) ? DMG_FOILBUDDHA : 0;
thingToHit = linetarget;
int newdam = P_DamageMobj (thingToHit, self->target, self->target, damage, spray != NULL? FName(spray->DamageType) : FName(NAME_BFGSplash),
spray != NULL && (spray->flags3 & MF3_FOILINVUL)? DMG_FOILINVUL : 0);
dmgFlagPass);
P_TraceBleed (newdam > 0 ? newdam : damage, thingToHit, self->target);
}
return 0;

View File

@ -76,6 +76,7 @@
#include "d_net.h"
#include "d_event.h"
#include "p_acs.h"
#include "p_effect.h"
#include "m_joy.h"
#include "farchive.h"
#include "r_renderer.h"
@ -115,6 +116,7 @@ CVAR (Bool, chasedemo, false, 0);
CVAR (Bool, storesavepic, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, longsavemessages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (String, save_dir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
CVAR (Bool, cl_waitforsave, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
EXTERN_CVAR (Float, con_midtime);
//==========================================================================
@ -140,6 +142,7 @@ gameaction_t gameaction;
gamestate_t gamestate = GS_STARTUP;
int paused;
bool pauseext;
bool sendpause; // send a pause event next tic
bool sendsave; // send a save event next tic
bool sendturn180; // [RH] send a 180 degree turn next tic
@ -161,6 +164,8 @@ int consoleplayer; // player taking events
int gametic;
CVAR(Bool, demo_compress, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
FString newdemoname;
FString newdemomap;
FString demoname;
bool demorecording;
bool demoplayback;
@ -877,7 +882,7 @@ static void ChangeSpy (int changespy)
pnum &= MAXPLAYERS-1;
if (playeringame[pnum] &&
(!checkTeam || players[pnum].mo->IsTeammate (players[consoleplayer].mo) ||
(bot_allowspy && players[pnum].isbot)))
(bot_allowspy && players[pnum].Bot != NULL)))
{
break;
}
@ -1016,10 +1021,16 @@ void G_Ticker ()
// do player reborns if needed
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] &&
(players[i].playerstate == PST_REBORN || players[i].playerstate == PST_ENTER))
if (playeringame[i])
{
G_DoReborn (i, false);
if ((players[i].playerstate == PST_GONE))
{
G_DoPlayerPop(i);
}
if ((players[i].playerstate == PST_REBORN || players[i].playerstate == PST_ENTER))
{
G_DoReborn(i, false);
}
}
}
@ -1044,6 +1055,10 @@ void G_Ticker ()
case ga_loadlevel:
G_DoLoadLevel (-1, false);
break;
case ga_recordgame:
G_CheckDemoStatus();
G_RecordDemo(newdemoname);
G_BeginRecording(newdemomap);
case ga_newgame2: // Silence GCC (see above)
case ga_newgame:
G_DoNewGame ();
@ -1117,6 +1132,9 @@ void G_Ticker ()
// check, not just the player's x position like BOOM.
DWORD rngsum = FRandom::StaticSumSeeds ();
//Added by MC: For some of that bot stuff. The main bot function.
bglobal.Main ();
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
@ -1136,13 +1154,13 @@ void G_Ticker ()
// If the user alt-tabbed away, paused gets set to -1. In this case,
// we do not want to read more demo commands until paused is no
// longer negative.
if (demoplayback && paused >= 0)
if (demoplayback)
{
G_ReadDemoTiccmd (cmd, i);
}
else
{
memcpy (cmd, newcmd, sizeof(ticcmd_t));
memcpy(cmd, newcmd, sizeof(ticcmd_t));
}
// check for turbo cheats
@ -1152,7 +1170,7 @@ void G_Ticker ()
Printf ("%s is turbo!\n", players[i].userinfo.GetName());
}
if (netgame && !players[i].isbot && !demoplayback && (gametic%ticdup) == 0)
if (netgame && players[i].Bot == NULL && !demoplayback && (gametic%ticdup) == 0)
{
//players[i].inconsistant = 0;
if (gametic > BACKUPTICS*ticdup && consistancy[i][buf] != cmd->consistancy)
@ -1334,10 +1352,10 @@ void G_PlayerReborn (int player)
int chasecam;
BYTE currclass;
userinfo_t userinfo; // [RH] Save userinfo
botskill_t b_skill; //Added by MC:
APlayerPawn *actor;
PClassPlayerPawn *cls;
FString log;
DBot *Bot; //Added by MC:
p = &players[player];
@ -1347,18 +1365,19 @@ void G_PlayerReborn (int player)
itemcount = p->itemcount;
secretcount = p->secretcount;
currclass = p->CurrentPlayerClass;
b_skill = p->skill; //Added by MC:
userinfo.TransferFrom(p->userinfo);
actor = p->mo;
cls = p->cls;
log = p->LogText;
chasecam = p->cheats & CF_CHASECAM;
Bot = p->Bot; //Added by MC:
// Reset player structure to its defaults
p->~player_t();
::new(p) player_t;
memcpy (p->frags, frags, sizeof(p->frags));
p->health = actor->health;
p->fragcount = fragcount;
p->killcount = killcount;
p->itemcount = itemcount;
@ -1369,8 +1388,7 @@ void G_PlayerReborn (int player)
p->cls = cls;
p->LogText = log;
p->cheats |= chasecam;
p->skill = b_skill; //Added by MC:
p->Bot = Bot; //Added by MC:
p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately
p->original_oldbuttons = ~0;
@ -1378,15 +1396,19 @@ void G_PlayerReborn (int player)
if (gamestate != GS_TITLELEVEL)
{
// [GRB] Give inventory specified in DECORATE
actor->GiveDefaultInventory ();
p->ReadyWeapon = p->PendingWeapon;
}
//Added by MC: Init bot structure.
if (bglobal.botingame[player])
bglobal.CleanBotstuff (p);
else
p->isbot = false;
//Added by MC: Init bot structure.
if (p->Bot != NULL)
{
botskill_t skill = p->Bot->skill;
p->Bot->Clear ();
p->Bot->player = p;
p->Bot->skill = skill;
}
}
//
@ -1661,6 +1683,56 @@ void G_DoReborn (int playernum, bool freshbot)
}
}
//
// G_DoReborn
//
void G_DoPlayerPop(int playernum)
{
playeringame[playernum] = false;
if (deathmatch)
{
Printf("%s left the game with %d frags\n",
players[playernum].userinfo.GetName(),
players[playernum].fragcount);
}
else
{
Printf("%s left the game\n", players[playernum].userinfo.GetName());
}
// [RH] Revert each player to their own view if spying through the player who left
for (int ii = 0; ii < MAXPLAYERS; ++ii)
{
if (playeringame[ii] && players[ii].camera == players[playernum].mo)
{
players[ii].camera = players[ii].mo;
if (ii == consoleplayer && StatusBar != NULL)
{
StatusBar->AttachToPlayer(&players[ii]);
}
}
}
// [RH] Make the player disappear
FBehavior::StaticStopMyScripts(players[playernum].mo);
if (players[playernum].mo != NULL)
{
P_DisconnectEffect(players[playernum].mo);
players[playernum].mo->player = NULL;
players[playernum].mo->Destroy();
if (!(players[playernum].mo->ObjectFlags & OF_EuthanizeMe))
{ // We just destroyed a morphed player, so now the original player
// has taken their place. Destroy that one too.
players[playernum].mo->Destroy();
}
players[playernum].mo = NULL;
players[playernum].camera = NULL;
}
// [RH] Let the scripts know the player left
FBehavior::StaticStartTypedScripts(SCRIPT_Disconnect, NULL, true, playernum);
}
void G_ScreenShot (char *filename)
{
shotfile = filename;
@ -2079,6 +2151,9 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
filename = G_BuildSaveName ("demosave.zds", -1);
}
if (cl_waitforsave)
I_FreezeTime(true);
insave = true;
G_SnapshotLevel ();
@ -2088,6 +2163,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
{
Printf ("Could not create savegame '%s'\n", filename.GetChars());
insave = false;
I_FreezeTime(false);
return;
}
@ -2164,6 +2240,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
}
insave = false;
I_FreezeTime(false);
}
@ -2376,6 +2453,16 @@ void G_DeferedPlayDemo (const char *name)
CCMD (playdemo)
{
if (netgame)
{
Printf("End your current netgame first!");
return;
}
if (demorecording)
{
Printf("End your current demo first!");
return;
}
if (argv.argc() > 1)
{
G_DeferedPlayDemo (argv[1]);
@ -2558,7 +2645,7 @@ void G_DoPlayDemo (void)
{
FixPathSeperator (defdemoname);
DefaultExtension (defdemoname, ".lmp");
M_ReadFile (defdemoname, &demobuffer);
M_ReadFileMalloc (defdemoname, &demobuffer);
}
demo_p = demobuffer;

View File

@ -81,6 +81,7 @@ enum EFinishLevelType
void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags);
void G_DoReborn (int playernum, bool freshbot);
void G_DoPlayerPop(int playernum);
// Adds pitch to consoleplayer's viewpitch and clamps it
void G_AddViewPitch (int look);

View File

@ -28,10 +28,14 @@ int AWhirlwind::DoSpecialDamage (AActor *target, int damage, FName damagetype)
{
int randVal;
target->angle += pr_foo.Random2() << 20;
target->velx += pr_foo.Random2() << 10;
target->vely += pr_foo.Random2() << 10;
if ((level.time & 16) && !(target->flags2 & MF2_BOSS))
if (!(target->flags7 & MF7_DONTTHRUST))
{
target->angle += pr_foo.Random2() << 20;
target->velx += pr_foo.Random2() << 10;
target->vely += pr_foo.Random2() << 10;
}
if ((level.time & 16) && !(target->flags2 & MF2_BOSS) && !(target->flags7 & MF7_DONTTHRUST))
{
randVal = pr_foo();
if (randVal > 160)

View File

@ -60,7 +60,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CMaceAttack)
}
}
// didn't find any creatures, so try to strike any walls
player->mo->special1 = 0;
player->mo->weaponspecial = 0;
angle = player->mo->angle;
slope = P_AimLineAttack (player->mo, angle, MELEERANGE, &linetarget);

View File

@ -177,7 +177,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffInitBlink)
{
PARAM_ACTION_PROLOGUE;
self->special1 = (pr_blink()>>1)+20;
self->weaponspecial = (pr_blink()>>1)+20;
return 0;
}
@ -193,10 +193,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_CStaffCheckBlink)
if (self->player && self->player->ReadyWeapon)
{
if (!--self->special1)
if (!--self->weaponspecial)
{
P_SetPsprite (self->player, ps_weapon, self->player->ReadyWeapon->FindState ("Blink"));
self->special1 = (pr_blink()+50)>>2;
self->weaponspecial = (pr_blink()+50)>>2;
}
else
{

View File

@ -270,7 +270,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FAxeAttack)
}
}
// didn't find any creatures, so try to strike any walls
pmo->special1 = 0;
pmo->weaponspecial = 0;
angle = pmo->angle;
slope = P_AimLineAttack (pmo, angle, MELEERANGE, &linetarget);

View File

@ -61,7 +61,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerAttack)
{
P_ThrustMobj(linetarget, angle, power);
}
pmo->special1 = false; // Don't throw a hammer
pmo->weaponspecial = false; // Don't throw a hammer
goto hammerdone;
}
}
@ -77,7 +77,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerAttack)
{
P_ThrustMobj(linetarget, angle, power);
}
pmo->special1 = false; // Don't throw a hammer
pmo->weaponspecial = false; // Don't throw a hammer
goto hammerdone;
}
}
@ -87,11 +87,11 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerAttack)
slope = P_AimLineAttack (pmo, angle, HAMMER_RANGE, &linetarget, 0, ALF_CHECK3D);
if (P_LineAttack (pmo, angle, HAMMER_RANGE, slope, damage, NAME_Melee, hammertime, true) != NULL)
{
pmo->special1 = false;
pmo->weaponspecial = false;
}
else
{
pmo->special1 = true;
pmo->weaponspecial = true;
}
hammerdone:
// Don't spawn a hammer if the player doesn't have enough mana
@ -99,7 +99,7 @@ hammerdone:
!player->ReadyWeapon->CheckAmmo (player->ReadyWeapon->bAltFire ?
AWeapon::AltFire : AWeapon::PrimaryFire, false, true))
{
pmo->special1 = false;
pmo->weaponspecial = false;
}
return 0;
}
@ -122,7 +122,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FHammerThrow)
return 0;
}
if (!player->mo->special1)
if (!player->mo->weaponspecial)
{
return 0;
}

View File

@ -66,7 +66,7 @@ static bool TryPunch(APlayerPawn *pmo, angle_t angle, int damage, fixed_t power)
slope = P_AimLineAttack (pmo, angle, 2*MELEERANGE, &linetarget);
if (linetarget != NULL)
{
if (++pmo->special1 >= 3)
if (++pmo->weaponspecial >= 3)
{
damage <<= 1;
power *= 3;
@ -119,9 +119,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_FPunchAttack)
if (TryPunch(pmo, pmo->angle + i*(ANG45/16), damage, power) ||
TryPunch(pmo, pmo->angle - i*(ANG45/16), damage, power))
{ // hit something
if (pmo->special1 >= 3)
if (pmo->weaponspecial >= 3)
{
pmo->special1 = 0;
pmo->weaponspecial = 0;
P_SetPsprite (player, ps_weapon, player->ReadyWeapon->FindState("Fire2"));
S_Sound (pmo, CHAN_VOICE, "*fistgrunt", 1, ATTN_NORM);
}
@ -129,7 +129,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_FPunchAttack)
}
}
// didn't find any creatures, so try to strike any walls
pmo->special1 = 0;
pmo->weaponspecial = 0;
AActor *linetarget;
int slope = P_AimLineAttack (pmo, pmo->angle, MELEERANGE, &linetarget);

View File

@ -199,6 +199,46 @@ CCMD (map)
//
//==========================================================================
CCMD(recordmap)
{
if (netgame)
{
Printf("You cannot record a new game while in a netgame.");
return;
}
if (argv.argc() > 2)
{
try
{
if (!P_CheckMapData(argv[2]))
{
Printf("No map %s\n", argv[2]);
}
else
{
G_DeferedInitNew(argv[2]);
gameaction = ga_recordgame;
newdemoname = argv[1];
newdemomap = argv[2];
}
}
catch (CRecoverableError &error)
{
if (error.GetMessage())
Printf("%s", error.GetMessage());
}
}
else
{
Printf("Usage: recordmap <filename> <map name>\n");
}
}
//==========================================================================
//
//
//==========================================================================
CCMD (open)
{
if (netgame)
@ -236,6 +276,18 @@ void G_NewInit ()
{
int i;
// Destory all old player refrences that may still exist
TThinkerIterator<APlayerPawn> it(STAT_TRAVELLING);
APlayerPawn *pawn, *next;
next = it.Next();
while ((pawn = next) != NULL)
{
next = it.Next();
pawn->flags |= MF_NOSECTOR | MF_NOBLOCKMAP;
pawn->Destroy();
}
G_ClearSnapshots ();
ST_SetNeedRefresh();
netgame = false;
@ -499,7 +551,9 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill
}
else if (strncmp(levelname, "enDSeQ", 6) != 0)
{
nextinfo = FindLevelInfo (levelname, false);
FString reallevelname = levelname;
CheckWarpTransMap(reallevelname, true);
nextinfo = FindLevelInfo (reallevelname, false);
if (nextinfo != NULL)
{
level_info_t *nextredir = nextinfo->CheckLevelRedirect();
@ -507,8 +561,12 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill
{
nextinfo = nextredir;
}
nextlevel = nextinfo->MapName;
}
else
{
nextlevel = levelname;
}
nextlevel = nextinfo->MapName;
}
else
{
@ -1106,6 +1164,8 @@ void G_StartTravel ()
}
}
}
bglobal.StartTravel ();
}
//==========================================================================
@ -1169,6 +1229,7 @@ void G_FinishTravel ()
pawn->lastenemy = NULL;
pawn->player->mo = pawn;
pawn->player->camera = pawn;
pawn->flags2 &= ~MF2_BLASTED;
DObject::StaticPointerSubstitution (oldpawn, pawn);
oldpawn->Destroy();
pawndup->Destroy ();
@ -1176,6 +1237,15 @@ void G_FinishTravel ()
pawn->AddToHash ();
pawn->SetState(pawn->SpawnState);
pawn->player->SendPitchLimits();
// Sync the FLY flags.
if (pawn->flags2 & MF2_FLY)
{
pawn->player->cheats |= CF_FLY;
}
else
{
pawn->player->cheats &= ~CF_FLY;
}
for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory)
{
@ -1193,6 +1263,8 @@ void G_FinishTravel ()
}
}
}
bglobal.FinishTravel ();
}
//==========================================================================

View File

@ -50,7 +50,7 @@ class FScanner;
#define GCC_YSEG
#else
#define MSVC_YSEG
#define GCC_YSEG __attribute__((section(SECTION_YREG)))
#define GCC_YSEG __attribute__((section(SECTION_YREG))) __attribute__((used))
#endif
struct FIntermissionDescriptor;
@ -523,7 +523,7 @@ level_info_t *CheckLevelRedirect (level_info_t *info);
FString CalcMapName (int episode, int level);
void G_ParseMapInfo (const char *basemapinfo);
void G_ParseMapInfo (FString basemapinfo);
void G_ClearSnapshots (void);
void P_RemoveDefereds ();

View File

@ -1306,6 +1306,7 @@ MapFlagHandlers[] =
{ "compat_maskedmidtex", MITYPE_COMPATFLAG, COMPATF_MASKEDMIDTEX, 0 },
{ "compat_badangles", MITYPE_COMPATFLAG, 0, COMPATF2_BADANGLES },
{ "compat_floormove", MITYPE_COMPATFLAG, 0, COMPATF2_FLOORMOVE },
{ "compat_soundcutoff", MITYPE_COMPATFLAG, 0, COMPATF2_SOUNDCUTOFF },
{ "cd_start_track", MITYPE_EATNEXT, 0, 0 },
{ "cd_end1_track", MITYPE_EATNEXT, 0, 0 },
{ "cd_end2_track", MITYPE_EATNEXT, 0, 0 },
@ -1886,7 +1887,7 @@ static void ClearMapinfo()
//
//==========================================================================
void G_ParseMapInfo (const char *basemapinfo)
void G_ParseMapInfo (FString basemapinfo)
{
int lump, lastlump = 0;
level_info_t gamedefaults;
@ -1895,7 +1896,7 @@ void G_ParseMapInfo (const char *basemapinfo)
atterm(ClearMapinfo);
// Parse the default MAPINFO for the current game. This lump *MUST* come from zdoom.pk3.
if (basemapinfo != NULL)
if (basemapinfo.IsNotEmpty())
{
FMapInfoParser parse;
level_info_t defaultinfo;
@ -1903,7 +1904,7 @@ void G_ParseMapInfo (const char *basemapinfo)
if (Wads.GetLumpFile(baselump) > 0)
{
I_FatalError("File %s is overriding core lump %s.",
Wads.GetWadFullName(Wads.GetLumpFile(baselump)), basemapinfo);
Wads.GetWadFullName(Wads.GetLumpFile(baselump)), basemapinfo.GetChars());
}
parse.ParseMapInfo(baselump, gamedefaults, defaultinfo);
}

View File

@ -25,6 +25,11 @@ void ABasicArmor::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << SavePercent << BonusCount << MaxAbsorb << MaxFullAbsorb << AbsorbCount << ArmorType;
if (SaveVersion >= 4511)
{
arc << ActualSaveAmount;
}
}
//===========================================================================
@ -69,6 +74,7 @@ AInventory *ABasicArmor::CreateCopy (AActor *other)
copy->Icon = Icon;
copy->BonusCount = BonusCount;
copy->ArmorType = ArmorType;
copy->ActualSaveAmount = ActualSaveAmount;
GoAwayAndDie ();
return copy;
}
@ -268,6 +274,7 @@ bool ABasicArmorPickup::Use (bool pickup)
armor->MaxAbsorb = MaxAbsorb;
armor->MaxFullAbsorb = MaxFullAbsorb;
armor->ArmorType = this->GetClass()->TypeName;
armor->ActualSaveAmount = SaveAmount;
return true;
}
@ -360,6 +367,7 @@ bool ABasicArmorBonus::Use (bool pickup)
armor->MaxAbsorb = MaxAbsorb;
armor->ArmorType = this->GetClass()->TypeName;
armor->MaxFullAbsorb = MaxFullAbsorb;
armor->ActualSaveAmount = MaxSaveAmount;
}
armor->Amount = MIN(armor->Amount + saveAmount, MaxSaveAmount + armor->BonusCount);
@ -508,7 +516,14 @@ void AHexenArmor::AbsorbDamage (int damage, FName damageType, int &newdamage)
// with the dragon skin bracers.
if (damage < 10000)
{
#if __APPLE__ && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ == 1
// -O1 optimizer bug work around. Only needed for
// GCC 4.2.1 on OS X for 10.4/10.5 tools compatibility.
volatile fixed_t tmp = 300;
Slots[i] -= Scale (damage, SlotsIncrement[i], tmp);
#else
Slots[i] -= Scale (damage, SlotsIncrement[i], 300);
#endif
if (Slots[i] < 2*FRACUNIT)
{
Slots[i] = 0;

View File

@ -60,7 +60,8 @@ bool APowerupGiver::Use (bool pickup)
}
if (BlendColor != 0)
{
power->BlendColor = BlendColor;
if (BlendColor != MakeSpecialColormap(65535)) power->BlendColor = BlendColor;
else power->BlendColor = 0;
}
if (Mode != NAME_None)
{
@ -1296,6 +1297,18 @@ void APowerTargeter::InitEffect ()
PositionAccuracy ();
}
bool APowerTargeter::HandlePickup(AInventory *item)
{
if (Super::HandlePickup(item))
{
InitEffect(); // reset the HUD sprites
return true;
}
return false;
}
void APowerTargeter::DoEffect ()
{
Super::DoEffect ();
@ -1384,6 +1397,42 @@ void APowerFrightener::EndEffect ()
Owner->player->cheats &= ~CF_FRIGHTENING;
}
// Buddha Powerup --------------------------------
IMPLEMENT_CLASS (APowerBuddha)
//===========================================================================
//
// APowerBuddha :: InitEffect
//
//===========================================================================
void APowerBuddha::InitEffect ()
{
Super::InitEffect();
if (Owner== NULL || Owner->player == NULL)
return;
Owner->player->cheats |= CF_BUDDHA;
}
//===========================================================================
//
// APowerBuddha :: EndEffect
//
//===========================================================================
void APowerBuddha::EndEffect ()
{
Super::EndEffect();
if (Owner== NULL || Owner->player == NULL)
return;
Owner->player->cheats &= ~CF_BUDDHA;
}
// Scanner powerup ----------------------------------------------------------
IMPLEMENT_CLASS (APowerScanner)

View File

@ -173,6 +173,7 @@ protected:
void EndEffect ();
void PositionAccuracy ();
void Travelled ();
bool HandlePickup(AInventory *item);
};
class APowerFrightener : public APowerup
@ -183,6 +184,14 @@ protected:
void EndEffect ();
};
class APowerBuddha : public APowerup
{
DECLARE_CLASS (APowerBuddha, APowerup)
protected:
void InitEffect ();
void EndEffect ();
};
class APowerTimeFreezer : public APowerup
{
DECLARE_CLASS( APowerTimeFreezer, APowerup )

View File

@ -12,6 +12,7 @@
#include "doomstat.h"
#include "g_level.h"
#include "farchive.h"
#include "p_enemy.h"
static FRandom pr_morphmonst ("MorphMonster");
@ -527,11 +528,11 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor
if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster)))
{
AMorphedMonster *fakeme = static_cast<AMorphedMonster *>(actor);
AActor *realme = fakeme->UnmorphedMe;
if ((fakeme->UnmorphTime) &&
(fakeme->MorphStyle & MORPH_UNDOBYDEATH) &&
(fakeme->UnmorphedMe))
(realme))
{
AActor *realme = fakeme->UnmorphedMe;
int realstyle = fakeme->MorphStyle;
int realhealth = fakeme->health;
if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
@ -542,6 +543,14 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor
return true;
}
}
if (realme->flags4 & MF4_BOSSDEATH)
{
realme->health = 0; // make sure that A_BossDeath considers it dead.
// FIXME: Use the caller's stack once the whole chain is scriptable.
VMFrameStack stack;
VMValue params[3] = { realme, realme, VMValue(NULL, ATAG_STATE) };
stack.Call(A_BossDeath_VMPtr, params, countof(params), NULL, 0, NULL);
}
fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die()
return false;
}

View File

@ -545,7 +545,7 @@ bool AInventory::ShouldRespawn ()
{
if ((ItemFlags & IF_BIGPOWERUP) && !(dmflags2 & DF2_RESPAWN_SUPER)) return false;
if (ItemFlags & IF_NEVERRESPAWN) return false;
return !!(dmflags & DF_ITEMS_RESPAWN);
return !!((dmflags & DF_ITEMS_RESPAWN) || (ItemFlags & IF_ALWAYSRESPAWN));
}
//===========================================================================
@ -1070,8 +1070,8 @@ void AInventory::Touch (AActor *toucher)
//Added by MC: Check if item taken was the roam destination of any bot
for (int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && this == players[i].dest)
players[i].dest = NULL;
if (players[i].Bot != NULL && this == players[i].Bot->dest)
players[i].Bot->dest = NULL;
}
}

View File

@ -126,6 +126,7 @@ enum
IF_NEVERRESPAWN = 1<<20, // Never, ever respawns
IF_NOSCREENFLASH = 1<<21, // No pickup flash on the player's screen
IF_TOSSED = 1<<22, // Was spawned by P_DropItem (i.e. as a monster drop)
IF_ALWAYSRESPAWN = 1<<23, // Always respawn, regardless of dmflag
};
@ -455,6 +456,7 @@ public:
int MaxFullAbsorb;
int BonusCount;
FNameNoInit ArmorType;
int ActualSaveAmount;
};
// BasicArmorPickup replaces the armor you have.

View File

@ -444,7 +444,7 @@ int FMugShot::UpdateState(player_t *player, StateFlags stateflags)
if (bNormal)
{
bool good;
if ((player->cheats & CF_GODMODE) || (player->mo != NULL && player->mo->flags2 & MF2_INVULNERABLE))
if ((player->cheats & CF_GODMODE) || (player->cheats & CF_GODMODE2) || (player->mo != NULL && player->mo->flags2 & MF2_INVULNERABLE))
{
good = SetState((stateflags & ANIMATEDGODMODE) ? "godanimated" : "god");
}

View File

@ -886,8 +886,11 @@ class CommandDrawString : public SBarInfoCommand
}
break;
case TIME:
str.Format("%02d:%02d:%02d", (level.time/TICRATE)/3600, ((level.time/TICRATE)%3600)/60, (level.time/TICRATE)%60);
{
int sec = Tics2Seconds(level.time);
str.Format("%02d:%02d:%02d", sec / 3600, (sec % 3600) / 60, sec % 60);
break;
}
case LOGTEXT:
str = statusBar->CPlayer->LogText;
break;
@ -3380,43 +3383,16 @@ class CommandInInventory : public SBarInfoCommandFlowControl
AInventory *invItem[2] = { statusBar->CPlayer->mo->FindInventory(item[0]), statusBar->CPlayer->mo->FindInventory(item[1]) };
if (invItem[0] != NULL && amount[0] > 0 && invItem[0]->Amount < amount[0]) invItem[0] = NULL;
if (invItem[1] != NULL && amount[1] > 0 && invItem[1]->Amount < amount[1]) invItem[1] = NULL;
if(invItem[1] != NULL && conditionAnd)
if (item[1])
{
if((invItem[0] != NULL && invItem[1] != NULL) && !negate)
{
SetTruth(true, block, statusBar);
return;
}
else if((invItem[0] == NULL || invItem[1] == NULL) && negate)
{
SetTruth(true, block, statusBar);
return;
}
if (conditionAnd)
SetTruth((invItem[0] && invItem[1]) != negate, block, statusBar);
else
SetTruth((invItem[0] || invItem[1]) != negate, block, statusBar);
}
else if(invItem[1] != NULL && !conditionAnd)
{
if((invItem[0] != NULL || invItem[1] != NULL) && !negate)
{
SetTruth(true, block, statusBar);
return;
}
else if((invItem[0] == NULL && invItem[1] == NULL) && negate)
{
SetTruth(true, block, statusBar);
return;
}
}
else if((invItem[0] != NULL) && !negate)
{
SetTruth(true, block, statusBar);
return;
}
else if((invItem[0] == NULL) && negate)
{
SetTruth(true, block, statusBar);
return;
}
SetTruth(false, block, statusBar);
else
SetTruth((invItem[0] != NULL) != negate, block, statusBar);
}
protected:
bool conditionAnd;
@ -3455,6 +3431,46 @@ class CommandAlpha : public SBarInfoMainBlock
////////////////////////////////////////////////////////////////////////////////
class CommandIfHealth : public SBarInfoCommandFlowControl
{
public:
CommandIfHealth(SBarInfo *script) : SBarInfoCommandFlowControl(script),
negate(false), percentage(false)
{
}
void Parse(FScanner &sc, bool fullScreenOffsets)
{
if (sc.CheckToken(TK_Identifier))
{
if (sc.Compare("not"))
negate = true;
else
sc.ScriptError("Expected 'not', but got '%s' instead.", sc.String);
}
sc.MustGetToken(TK_IntConst);
percentage = sc.CheckToken('%');
hpamount = sc.Number;
SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets);
}
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
{
SBarInfoCommandFlowControl::Tick(block, statusBar, hudChanged);
int phealth = percentage ? statusBar->CPlayer->mo->health * 100 / statusBar->CPlayer->mo->GetMaxHealth() : statusBar->CPlayer->mo->health;
SetTruth((phealth >= hpamount) ^ negate, block, statusBar);
}
protected:
bool negate;
bool percentage;
int hpamount;
};
////////////////////////////////////////////////////////////////////////////////
static const char *SBarInfoCommandNames[] =
{
"drawimage", "drawnumber", "drawswitchableimage",
@ -3464,7 +3480,7 @@ static const char *SBarInfoCommandNames[] =
"gamemode", "playerclass", "playertype", "aspectratio",
"isselected", "usesammo", "usessecondaryammo",
"hasweaponpiece", "inventorybarnotvisible",
"weaponammo", "ininventory", "alpha",
"weaponammo", "ininventory", "alpha", "ifhealth",
NULL
};
@ -3477,7 +3493,7 @@ enum SBarInfoCommands
SBARINFO_GAMEMODE, SBARINFO_PLAYERCLASS, SBARINFO_PLAYERTYPE, SBARINFO_ASPECTRATIO,
SBARINFO_ISSELECTED, SBARINFO_USESAMMO, SBARINFO_USESSECONDARYAMMO,
SBARINFO_HASWEAPONPIECE, SBARINFO_INVENTORYBARNOTVISIBLE,
SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, SBARINFO_ALPHA,
SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, SBARINFO_ALPHA, SBARINFO_IFHEALTH,
};
SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc)
@ -3510,6 +3526,7 @@ SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc)
case SBARINFO_WEAPONAMMO: return new CommandWeaponAmmo(script);
case SBARINFO_ININVENTORY: return new CommandInInventory(script);
case SBARINFO_ALPHA: return new CommandAlpha(script);
case SBARINFO_IFHEALTH: return new CommandIfHealth(script);
}
sc.ScriptError("Unknown command '%s'.\n", sc.String);

View File

@ -37,6 +37,7 @@
// copy would be.
#include "doomtype.h"
#include "doomdef.h"
#include "v_video.h"
#include "gi.h"
#include "c_cvars.h"
@ -48,6 +49,7 @@
#include "p_local.h"
#include "doomstat.h"
#include "g_level.h"
#include "d_net.h"
#include <time.h>
@ -73,6 +75,7 @@ CVAR (Bool, hud_showscore, false, CVAR_ARCHIVE); // for user maintained score
CVAR (Bool, hud_showweapons, true, CVAR_ARCHIVE); // Show weapons collected
CVAR (Int , hud_showtime, 0, CVAR_ARCHIVE); // Show time on HUD
CVAR (Int , hud_timecolor, CR_GOLD,CVAR_ARCHIVE); // Color of in-game time on HUD
CVAR (Int , hud_showlag, 0, CVAR_ARCHIVE); // Show input latency (maketic - gametic difference)
CVAR (Int, hud_ammo_red, 25, CVAR_ARCHIVE) // ammo percent less than which status is red
CVAR (Int, hud_ammo_yellow, 50, CVAR_ARCHIVE) // ammo percent less is yellow more green
@ -866,7 +869,7 @@ static void DrawTime()
: (hud_showtime < 6
? level.time
: level.totaltime);
const int timeSeconds = timeTicks / TICRATE;
const int timeSeconds = Tics2Seconds(timeTicks);
hours = timeSeconds / 3600;
minutes = (timeSeconds % 3600) / 60;
@ -916,6 +919,51 @@ static void DrawTime()
DrawHudText(SmallFont, hud_timecolor, timeString, hudwidth - width, height, FRACUNIT);
}
//---------------------------------------------------------------------------
//
// Draw in-game latency
//
//---------------------------------------------------------------------------
static void DrawLatency()
{
if (hud_showlag <= 0 ||
(hud_showlag == 1 && !netgame) ||
hud_showlag > 2)
{
return;
}
int i, localdelay = 0, arbitratordelay = 0;
for (i = 0; i < BACKUPTICS; i++) localdelay += netdelay[0][i];
for (i = 0; i < BACKUPTICS; i++) arbitratordelay += netdelay[nodeforplayer[Net_Arbitrator]][i];
localdelay = ((localdelay / BACKUPTICS) * ticdup) * (1000 / TICRATE);
arbitratordelay = ((arbitratordelay / BACKUPTICS) * ticdup) * (1000 / TICRATE);
int color = CR_GREEN;
if (MAX(localdelay, arbitratordelay) > 200)
{
color = CR_YELLOW;
}
if (MAX(localdelay, arbitratordelay) > 400)
{
color = CR_ORANGE;
}
if (MAX(localdelay, arbitratordelay) >= ((BACKUPTICS / 2 - 1) * ticdup) * (1000 / TICRATE))
{
color = CR_RED;
}
char tempstr[32];
const int millis = (level.time % TICRATE) * (1000 / TICRATE);
mysnprintf(tempstr, sizeof(tempstr), "a:%dms - l:%dms", arbitratordelay, localdelay);
const int characterCount = (int)strlen(tempstr);
const int width = SmallFont->GetCharWidth('0') * characterCount + 2; // small offset from screen's border
const int height = SmallFont->GetHeight() * 2;
DrawHudText(SmallFont, color, tempstr, hudwidth - width, height, FRACUNIT);
}
//---------------------------------------------------------------------------
//
@ -981,6 +1029,7 @@ void DrawHUD()
if (idmypos) DrawCoordinates(CPlayer);
DrawTime();
DrawLatency();
}
else
{
@ -993,7 +1042,7 @@ void DrawHUD()
if (am_showtotaltime)
{
seconds = level.totaltime / TICRATE;
seconds = Tics2Seconds(level.totaltime);
mysnprintf(printstr, countof(printstr), "%02i:%02i:%02i", seconds/3600, (seconds%3600)/60, seconds%60);
DrawHudText(SmallFont, hudcolor_ttim, printstr, hudwidth-length, bottom, FRACUNIT);
bottom -= fonth;
@ -1003,14 +1052,14 @@ void DrawHUD()
{
if (level.clusterflags&CLUSTER_HUB)
{
seconds = level.time /TICRATE;
seconds = Tics2Seconds(level.time);
mysnprintf(printstr, countof(printstr), "%02i:%02i:%02i", seconds/3600, (seconds%3600)/60, seconds%60);
DrawHudText(SmallFont, hudcolor_time, printstr, hudwidth-length, bottom, FRACUNIT);
bottom -= fonth;
}
// Single level time for hubs
seconds= level.maptime /TICRATE;
seconds= Tics2Seconds(level.maptime);
mysnprintf(printstr, countof(printstr), "%02i:%02i:%02i", seconds/3600, (seconds%3600)/60, seconds%60);
DrawHudText(SmallFont, hudcolor_ltim, printstr, hudwidth-length, bottom, FRACUNIT);
}

View File

@ -146,7 +146,6 @@ void ST_LoadCrosshair(bool alwaysload)
{
int num = 0;
char name[16], size;
int lump;
if (!crosshairforce &&
players[consoleplayer].camera != NULL &&
@ -179,18 +178,20 @@ void ST_LoadCrosshair(bool alwaysload)
num = -num;
}
size = (SCREENWIDTH < 640) ? 'S' : 'B';
mysnprintf (name, countof(name), "XHAIR%c%d", size, num);
if ((lump = Wads.CheckNumForName (name, ns_graphics)) == -1)
FTextureID texid = TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
if (!texid.isValid())
{
mysnprintf (name, countof(name), "XHAIR%c1", size);
if ((lump = Wads.CheckNumForName (name, ns_graphics)) == -1)
texid = TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
if (!texid.isValid())
{
strcpy (name, "XHAIRS1");
texid = TexMan.CheckForTexture("XHAIRS1", FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
}
num = 1;
}
CrosshairNum = num;
CrosshairImage = TexMan[TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch)];
CrosshairImage = TexMan[texid];
}
//---------------------------------------------------------------------------
@ -1122,7 +1123,7 @@ void DBaseStatusBar::DrawCrosshair ()
ST_LoadCrosshair();
// Don't draw the crosshair if there is none
if (CrosshairImage == NULL || gamestate == GS_TITLELEVEL)
if (CrosshairImage == NULL || gamestate == GS_TITLELEVEL || camera->health <= 0)
{
return;
}
@ -1306,8 +1307,8 @@ void DBaseStatusBar::Draw (EHudState state)
}
else if (automapactive)
{
int y, time = level.time / TICRATE, height;
int totaltime = level.totaltime / TICRATE;
int y, time = Tics2Seconds(level.time), height;
int totaltime = Tics2Seconds(level.totaltime);
EColorRange highlight = (gameinfo.gametype & GAME_DoomChex) ?
CR_UNTRANSLATED : CR_YELLOW;

View File

@ -114,7 +114,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_AlienSpectreDeath)
switch (self->GetClass()->TypeName)
{
case NAME_AlienSpectre1:
EV_DoFloor (DFloor::floorLowerToLowest, NULL, 999, FRACUNIT, 0, 0, 0, false);
EV_DoFloor (DFloor::floorLowerToLowest, NULL, 999, FRACUNIT, 0, -1, 0, false);
log = 95;
break;
@ -188,7 +188,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_AlienSpectreDeath)
{ // Another Sigil piece. Woohoo!
log = 83;
}
EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, 0, 0, false);
EV_DoFloor (DFloor::floorLowerToLowest, NULL, 666, FRACUNIT, 0, -1, 0, false);
break;
default:

View File

@ -94,7 +94,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CrusaderDeath)
if (CheckBossDeath (self))
{
EV_DoFloor (DFloor::floorLowerToLowest, NULL, 667, FRACUNIT, 0, 0, 0, false);
EV_DoFloor (DFloor::floorLowerToLowest, NULL, 667, FRACUNIT, 0, -1, 0, false);
}
return 0;
}

View File

@ -563,7 +563,7 @@ void APowerCoupling::Die (AActor *source, AActor *inflictor, int dmgflags)
P_NoiseAlert (source, this);
}
EV_DoDoor (DDoor::doorClose, NULL, players[i].mo, 225, 2*FRACUNIT, 0, 0, 0);
EV_DoFloor (DFloor::floorLowerToHighest, NULL, 44, FRACUNIT, 0, 0, 0, false);
EV_DoFloor (DFloor::floorLowerToHighest, NULL, 44, FRACUNIT, 0, -1, 0, false);
players[i].mo->GiveInventoryType (QuestItemClasses[5]);
S_Sound (CHAN_VOICE, "svox/voc13", 1, ATTN_NORM);
players[i].SetLogNumber (13);

View File

@ -586,29 +586,33 @@ private:
screen->DrawTexture (Images[back], left, top, DTA_CleanNoMove, true, DTA_Alpha, FRACUNIT*3/4, TAG_DONE);
screen->DrawTexture (Images[bars], left, top, DTA_CleanNoMove, true, TAG_DONE);
switch (CurrentPop)
{
case POP_Log:
{
int seconds = Tics2Seconds(level.time);
// Draw the latest log message.
mysnprintf (buff, countof(buff), "%02d:%02d:%02d",
(level.time/TICRATE)/3600,
((level.time/TICRATE)%3600)/60,
(level.time/TICRATE)%60);
mysnprintf(buff, countof(buff), "%02d:%02d:%02d",
seconds / 3600,
(seconds % 3600) / 60,
(seconds) % 60);
screen->DrawText (SmallFont2, CR_UNTRANSLATED, left+210*xscale, top+8*yscale, buff,
screen->DrawText(SmallFont2, CR_UNTRANSLATED, left + 210 * xscale, top + 8 * yscale, buff,
DTA_CleanNoMove, true, TAG_DONE);
if (CPlayer->LogText != NULL)
{
FBrokenLines *lines = V_BreakLines (SmallFont2, 272, CPlayer->LogText);
FBrokenLines *lines = V_BreakLines(SmallFont2, 272, CPlayer->LogText);
for (i = 0; lines[i].Width >= 0; ++i)
{
screen->DrawText (SmallFont2, CR_UNTRANSLATED, left+24*xscale, top+(18+i*12)*yscale,
screen->DrawText(SmallFont2, CR_UNTRANSLATED, left + 24 * xscale, top + (18 + i * 12)*yscale,
lines[i].Text, DTA_CleanNoMove, true, TAG_DONE);
}
V_FreeBrokenLines (lines);
V_FreeBrokenLines(lines);
}
break;
}
case POP_Keys:
// List the keys the player has.

View File

@ -41,7 +41,7 @@ static inline SDWORD Scale (SDWORD a, SDWORD b, SDWORD c)
: "a,a,a,a,a,a" (a),
"m,r,m,r,d,d" (b),
"r,r,m,m,r,m" (c)
: "%cc"
: "cc"
);
return result;
@ -59,7 +59,7 @@ static inline SDWORD MulScale (SDWORD a, SDWORD b, SDWORD c)
: "a,a,a,a" (a),
"m,r,m,r" (b),
"c,c,I,I" (c)
: "%cc"
: "cc"
);
return result;
}
@ -210,7 +210,7 @@ static inline SDWORD DivScale (SDWORD a, SDWORD b, SDWORD c)
: "a" (lo),
"d" (hi),
"r" (b)
: "%cc");
: "cc");
return result;
}
@ -226,7 +226,7 @@ static inline SDWORD DivScale1 (SDWORD a, SDWORD b)
"=&d,d" (dummy)
: "a,a" (a),
"r,m" (b)
: "%cc");
: "cc");
return result;
}
@ -241,7 +241,7 @@ static inline SDWORD DivScale1 (SDWORD a, SDWORD b)
: "a,a" (a<<s), \
"d,d" (a>>(32-s)), \
"r,m" (b) \
: "%cc"); \
: "cc"); \
return result; \
}
@ -287,7 +287,7 @@ static inline SDWORD DivScale32 (SDWORD a, SDWORD b)
"=d,d" (dummy)
: "d,d" (a),
"r,m" (b)
: "%cc");
: "cc");
return result;
}
@ -313,7 +313,7 @@ static inline void clearbufshort (void *buff, unsigned int count, WORD clear)
"rep stosw"
:"=D" (buff), "=c" (count)
:"D" (buff), "c" (count), "a" (clear|(clear<<16))
:"%cc");
:"cc");
}
static inline SDWORD ksgn (SDWORD a)
@ -327,6 +327,6 @@ static inline SDWORD ksgn (SDWORD a)
"adc $0,%1"
:"=r" (dummy), "=r" (result)
:"0" (a)
:"%cc");
:"cc");
return result;
}

View File

@ -290,6 +290,8 @@ void FMapInfoParser::ParseGameInfo()
else gameinfo.mCheatMapArrow = "";
}
// Insert valid keys here.
GAMEINFOKEY_STRING(mCheatKey, "cheatKey")
GAMEINFOKEY_STRING(mEasyKey, "easyKey")
GAMEINFOKEY_STRING(TitlePage, "titlePage")
GAMEINFOKEY_STRINGARRAY(creditPages, "addcreditPage", 8, false)
GAMEINFOKEY_STRINGARRAY(creditPages, "CreditPage", 8, true)

View File

@ -169,6 +169,7 @@ struct gameinfo_t
int TextScreenY;
FName DefaultEndSequence;
FString mMapArrow, mCheatMapArrow;
FString mEasyKey, mCheatKey;
FGIFont mStatscreenMapNameFont;
FGIFont mStatscreenFinishedFont;
FGIFont mStatscreenEnteringFont;

View File

@ -48,6 +48,8 @@
#include "d_player.h"
#include "hu_stuff.h"
#include "gstrings.h"
#include "d_net.h"
#include "c_dispatch.h"
// MACROS ------------------------------------------------------------------
@ -61,7 +63,7 @@
static void HU_DoDrawScores (player_t *, player_t *[MAXPLAYERS]);
static void HU_DrawTimeRemaining (int y);
static void HU_DrawPlayer (player_t *, bool, int, int, int, int, int, int, int, int);
static void HU_DrawPlayer(player_t *, bool, int, int, int, int, int, int, int, int, int);
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
@ -116,6 +118,8 @@ int STACK_ARGS compareteams (const void *arg1, const void *arg2)
return diff;
}
bool SB_ForceActive = false;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// CODE --------------------------------------------------------------------
@ -228,7 +232,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER
int maxnamewidth, maxscorewidth, maxiconheight;
int numTeams = 0;
int x, y, ypadding, bottom;
int col2, col3, col4;
int col2, col3, col4, col5;
if (deathmatch)
{
@ -309,12 +313,14 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER
const char *text_color = GStrings("SCORE_COLOR"),
*text_frags = GStrings(deathmatch ? "SCORE_FRAGS" : "SCORE_KILLS"),
*text_name = GStrings("SCORE_NAME");
*text_name = GStrings("SCORE_NAME"),
*text_delay = GStrings("SCORE_DELAY");
col2 = (SmallFont->StringWidth(text_color) + 8) * CleanXfac;
col3 = col2 + (SmallFont->StringWidth(text_frags) + 8) * CleanXfac;
col4 = col3 + maxscorewidth * CleanXfac;
x = (SCREENWIDTH >> 1) - ((maxnamewidth * CleanXfac + col4) >> 1);
col5 = col4 + (maxnamewidth + 8) * CleanXfac;
x = (SCREENWIDTH >> 1) - (((SmallFont->StringWidth(text_delay) * CleanXfac) + col5) >> 1);
screen->DrawText (SmallFont, color, x, y, text_color,
DTA_CleanNoMove, true, TAG_DONE);
@ -325,6 +331,9 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER
screen->DrawText (SmallFont, color, x + col4, y, text_name,
DTA_CleanNoMove, true, TAG_DONE);
screen->DrawText(SmallFont, color, x + col5, y, text_delay,
DTA_CleanNoMove, true, TAG_DONE);
y += height + 6 * CleanYfac;
bottom -= height;
@ -332,7 +341,7 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER
{
if (playeringame[sortedplayers[i] - players])
{
HU_DrawPlayer (sortedplayers[i], player==sortedplayers[i], x, col2, col3, col4, maxnamewidth, y, ypadding, lineheight);
HU_DrawPlayer(sortedplayers[i], player == sortedplayers[i], x, col2, col3, col4, col5, maxnamewidth, y, ypadding, lineheight);
y += lineheight + CleanYfac;
}
}
@ -377,7 +386,7 @@ static void HU_DrawTimeRemaining (int y)
//
//==========================================================================
static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, int col3, int col4, int maxnamewidth, int y, int ypadding, int height)
static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, int col3, int col4, int col5, int maxnamewidth, int y, int ypadding, int height)
{
int color;
char str[80];
@ -387,12 +396,13 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2,
// The teamplay mode uses colors to show teams, so we need some
// other way to do highlighting. And it may as well be used for
// all modes for the sake of consistancy.
screen->Dim(MAKERGB(200,245,255), 0.125f, col1 - 12*CleanXfac, y - 1, col4 + (maxnamewidth + 24)*CleanXfac, height + 2);
screen->Dim(MAKERGB(200,245,255), 0.125f, col1 - 12*CleanXfac, y - 1, col5 + (maxnamewidth + 24)*CleanXfac, height + 2);
}
col2 += col1;
col3 += col1;
col4 += col1;
col5 += col1;
color = HU_GetRowColor(player, highlight);
HU_DrawColorBar(col1, y, height, (int)(player - players));
@ -412,6 +422,18 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2,
screen->DrawText (SmallFont, color, col4, y + ypadding, player->userinfo.GetName(),
DTA_CleanNoMove, true, TAG_DONE);
int avgdelay = 0;
for (int i = 0; i < BACKUPTICS; i++)
{
avgdelay += netdelay[nodeforplayer[(int)(player - players)]][i];
}
avgdelay /= BACKUPTICS;
mysnprintf(str, countof(str), "%d", (avgdelay * ticdup) * (1000 / TICRATE));
screen->DrawText(SmallFont, color, col5, y + ypadding, str,
DTA_CleanNoMove, true, TAG_DONE);
if (teamplay && Teams[player->userinfo.GetTeam()].GetLogo().IsNotEmpty ())
{
FTexture *pic = TexMan[Teams[player->userinfo.GetTeam()].GetLogo().GetChars ()];
@ -473,3 +495,8 @@ int HU_GetRowColor(player_t *player, bool highlight)
}
}
}
CCMD (togglescoreboard)
{
SB_ForceActive = !SB_ForceActive;
}

View File

@ -52,6 +52,8 @@ void HU_GetPlayerWidths(int &maxnamewidth, int &maxscorewidth, int &maxiconheigh
void HU_DrawColorBar(int x, int y, int height, int playernum);
int HU_GetRowColor(player_t *player, bool hightlight);
extern bool SB_ForceActive;
// Sorting routines
int STACK_ARGS comparepoints(const void *arg1, const void *arg2);

View File

@ -110,6 +110,7 @@ const char *neterror (void);
enum
{
PRE_CONNECT, // Sent from guest to host for initial connection
PRE_KEEPALIVE,
PRE_DISCONNECT, // Sent from guest that aborts the game
PRE_ALLHERE, // Sent from host to guest when everybody has connected
PRE_CONACK, // Sent from host to guest to acknowledge PRE_CONNECT receipt
@ -134,8 +135,8 @@ struct PreGamePacket
};
struct
{
u_long address;
u_short port;
DWORD address;
WORD port;
BYTE player;
BYTE pad;
} machines[MAXNETNODES];
@ -208,11 +209,11 @@ void PacketSend (void)
{
I_FatalError("Netbuffer overflow!");
}
assert(!(doomcom.data[0] & NCMD_COMPRESSED));
uLong size = TRANSMIT_SIZE - 1;
if (doomcom.datalength >= 10)
{
assert(!(doomcom.data[0] & NCMD_COMPRESSED));
TransmitBuffer[0] = doomcom.data[0] | NCMD_COMPRESSED;
c = compress2(TransmitBuffer + 1, &size, doomcom.data + 1, doomcom.datalength - 1, 9);
size += 1;
@ -548,10 +549,15 @@ bool Host_CheckForConnects (void *userdata)
SendConAck (doomcom.numnodes, numplayers);
}
break;
case PRE_KEEPALIVE:
break;
}
}
if (doomcom.numnodes < numplayers)
{
// Send message to everyone as a keepalive
SendConAck(doomcom.numnodes, numplayers);
return false;
}
@ -654,6 +660,12 @@ void HostGame (int i)
numplayers = 2;
}
if (numplayers > MAXNETNODES)
{
I_FatalError("You cannot host a game with %d players. The limit is currently %d.", numplayers, MAXNETNODES);
return;
}
if (numplayers == 1)
{ // Special case: Only 1 player, so don't bother starting the network
netgame = false;
@ -822,6 +834,10 @@ bool Guest_WaitForOthers (void *userdata)
}
}
packet.Fake = PRE_FAKE;
packet.Message = PRE_KEEPALIVE;
PreSend(&packet, 2, &sendaddress[1]);
return false;
}
@ -938,11 +954,6 @@ bool I_InitNetwork (void)
doomcom.ticdup = 1;
}
if (Args->CheckParm ("-extratic"))
doomcom.extratics = 1;
else
doomcom.extratics = 0;
v = Args->CheckValue ("-port");
if (v)
{

View File

@ -99,6 +99,23 @@ void cht_DoCheat (player_t *player, int cheat)
msg = GStrings("TXT_BUDDHAOFF");
break;
case CHT_GOD2:
player->cheats ^= CF_GODMODE2;
if (player->cheats & CF_GODMODE2)
msg = GStrings("STSTR_DQD2ON");
else
msg = GStrings("STSTR_DQD2OFF");
ST_SetNeedRefresh();
break;
case CHT_BUDDHA2:
player->cheats ^= CF_BUDDHA2;
if (player->cheats & CF_BUDDHA2)
msg = GStrings("TXT_BUDDHA2ON");
else
msg = GStrings("TXT_BUDDHA2OFF");
break;
case CHT_NOCLIP:
player->cheats ^= CF_NOCLIP;
if (player->cheats & CF_NOCLIP)
@ -323,7 +340,6 @@ void cht_DoCheat (player_t *player, int cheat)
player->mo->Translation = TRANSLATION(TRANSLATION_Players, BYTE(player-players));
}
player->mo->DamageType = NAME_None;
// player->mo->GiveDefaultInventory();
if (player->ReadyWeapon != NULL)
{
P_SetPsprite(player, ps_weapon, player->ReadyWeapon->GetUpState());

View File

@ -137,6 +137,32 @@ int M_ReadFile (char const *name, BYTE **buffer)
return length;
}
//
// M_ReadFile (same as above but use malloc instead of new to allocate the buffer.)
//
int M_ReadFileMalloc (char const *name, BYTE **buffer)
{
int handle, count, length;
struct stat fileinfo;
BYTE *buf;
handle = open (name, O_RDONLY | O_BINARY, 0666);
if (handle == -1)
I_Error ("Couldn't read file %s", name);
if (fstat (handle,&fileinfo) == -1)
I_Error ("Couldn't read file %s", name);
length = fileinfo.st_size;
buf = (BYTE*)M_Malloc(length);
count = read (handle, buf, length);
close (handle);
if (count < length)
I_Error ("Couldn't read file %s", name);
*buffer = buf;
return length;
}
//---------------------------------------------------------------------------
//
// PROC M_FindResponseFile

View File

@ -33,6 +33,7 @@ extern FGameConfigFile *GameConfig;
bool M_WriteFile (char const *name, void *source, int length);
int M_ReadFile (char const *name, BYTE **buffer);
int M_ReadFileMalloc (char const *name, BYTE **buffer);
void M_FindResponseFile (void);
// [RH] M_ScreenShot now accepts a filename parameter.

View File

@ -300,6 +300,9 @@ xx(Abs)
xx(ACS_NamedExecuteWithResult)
xx(CallACS)
xx(Sqrt)
xx(CheckClass)
xx(IsPointerEqual)
xx(Pick)
// Various actor names which are used internally
xx(MapSpot)
@ -420,6 +423,7 @@ xx(Passuse)
xx(Repeatspecial)
xx(Conversation)
xx(Locknumber)
xx(Midtex3dimpassible)
xx(Playercross)
xx(Playeruse)

View File

@ -69,7 +69,13 @@ static const int PO_LINE_EXPLICIT = 5;
angle_t FNodeBuilder::PointToAngle (fixed_t x, fixed_t y)
{
const double rad2bam = double(1<<30) / M_PI;
#if defined __APPLE__ && !defined __llvm__
// Work-around for vectorization issue in Apple's GCC 4.x
// See https://gcc.gnu.org/wiki/Math_Optimization_Flags for details
long double ang = atan2l (double(y), double(x));
#else // !__APPLE__ || __llvm__
double ang = atan2 (double(y), double(x));
#endif // __APPLE__ && !__llvm__
return angle_t(ang * rad2bam) << 1;
}

View File

@ -323,7 +323,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3)
{
assert(numchips >= 1 && numchips <= countof(chips));
uint i;
IsOPL3 = (opl_core == 1 || opl_core == 2);
IsOPL3 = (opl_core == 1 || opl_core == 2 || opl_core == 3);
memset(chips, 0, sizeof(chips));
if (IsOPL3)
@ -332,7 +332,7 @@ int OPLio::OPLinit(uint numchips, bool stereo, bool initopl3)
}
for (i = 0; i < numchips; ++i)
{
OPLEmul *chip = IsOPL3 ? (opl_core == 1 ? DBOPLCreate(stereo) : JavaOPLCreate(stereo)) : YM3812Create(stereo);
OPLEmul *chip = IsOPL3 ? (opl_core == 1 ? DBOPLCreate(stereo) : (opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo);
if (chip == NULL)
{
break;

979
src/oplsynth/nukedopl3.cpp Normal file
View File

@ -0,0 +1,979 @@
/*
* Copyright (C) 2013-2014 Nuke.YKT
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
Nuked Yamaha YMF262(aka OPL3) emulator.
Thanks:
MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh):
Feedback and Rhythm part calculation information.
forums.submarine.org.uk(carbon14, opl3):
Tremolo and phase generator calculation information.
OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
OPL2 ROMs.
*/
//version 1.5
/* Changelog:
v1.1:
Vibrato's sign fix
v1.2:
Operator key fix
Corrected 4-operator mode
Corrected rhythm mode
Some small fixes
v1.2.1:
Small envelope generator fix
Removed EX_Get function(not used)
v1.3:
Complete rewrite
(Not released)
v1.4:
New envelope and waveform generator
Some small fixes.
(Not released)
v1.4.1:
Envelope generator rate calculation fix
(Not released)
v1.4.2:
Version for ZDoom.
v1.5:
Optimizations
*/
/* Verified:
Noise generator.
Waveform generator.
Envelope generator increase table.
Tremolo.
*/
/* TODO:
Verify:
kslrom[15] value(is it 128?).
Sustain level = 15.
Vibrato, Phase generator.
Rhythm part.
Envelope generator state switching(decay->sustain when egt = 1 and decay->release).
Feedback.
Register write.
4-operator.
*/
#include <stdlib.h>
#include <string.h>
#include "nukedopl3.h"
//
// Envelope generator
//
typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope);
typedef void(*envelope_genfunc)(opl_slot *slott);
Bit16s envelope_calcexp(Bit32u level) {
if (level > 0x1fff) {
level = 0x1fff;
}
return ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 1) >> (level >> 8);
}
Bit16s envelope_calcsin0(Bit16u phase, Bit16u envelope) {
phase &= 0x3ff;
Bit16u out = 0;
Bit16u neg = 0;
if (phase & 0x200 && (phase & 0x1ff)) {
phase--;
neg = ~0;
}
if (phase & 0x100) {
out = logsinrom[(phase & 0xff) ^ 0xff];
}
else {
out = logsinrom[phase & 0xff];
}
return envelope_calcexp(out + (envelope << 3)) ^ neg;
}
Bit16s envelope_calcsin1(Bit16u phase, Bit16u envelope) {
phase &= 0x3ff;
Bit16u out = 0;
if (phase & 0x200) {
out = 0x1000;
}
else if (phase & 0x100) {
out = logsinrom[(phase & 0xff) ^ 0xff];
}
else {
out = logsinrom[phase & 0xff];
}
return envelope_calcexp(out + (envelope << 3));
}
Bit16s envelope_calcsin2(Bit16u phase, Bit16u envelope) {
phase &= 0x3ff;
Bit16u out = 0;
if (phase & 0x100) {
out = logsinrom[(phase & 0xff) ^ 0xff];
}
else {
out = logsinrom[phase & 0xff];
}
return envelope_calcexp(out + (envelope << 3));
}
Bit16s envelope_calcsin3(Bit16u phase, Bit16u envelope) {
phase &= 0x3ff;
Bit16u out = 0;
if (phase & 0x100) {
out = 0x1000;
}
else {
out = logsinrom[phase & 0xff];
}
return envelope_calcexp(out + (envelope << 3));
}
Bit16s envelope_calcsin4(Bit16u phase, Bit16u envelope) {
phase &= 0x3ff;
Bit16u out = 0;
Bit16u neg = 0;
if ((phase & 0x300) == 0x100 && (phase & 0xff)) {
phase--;
neg = ~0;
}
if (phase & 0x200) {
out = 0x1000;
}
else if (phase & 0x80) {
out = logsinrom[((phase ^ 0xff) << 1) & 0xff];
}
else {
out = logsinrom[(phase << 1) & 0xff];
}
return envelope_calcexp(out + (envelope << 3)) ^ neg;
}
Bit16s envelope_calcsin5(Bit16u phase, Bit16u envelope) {
phase &= 0x3ff;
Bit16u out = 0;
if (phase & 0x200) {
out = 0x1000;
}
else if (phase & 0x80) {
out = logsinrom[((phase ^ 0xff) << 1) & 0xff];
}
else {
out = logsinrom[(phase << 1) & 0xff];
}
return envelope_calcexp(out + (envelope << 3));
}
Bit16s envelope_calcsin6(Bit16u phase, Bit16u envelope) {
phase &= 0x3ff;
Bit16u neg = 0;
if (phase & 0x200 && (phase & 0x1ff)) {
phase--;
neg = ~0;
}
return envelope_calcexp(envelope << 3) ^ neg;
}
Bit16s envelope_calcsin7(Bit16u phase, Bit16u envelope) {
phase &= 0x3ff;
Bit16u out = 0;
Bit16u neg = 0;
if (phase & 0x200 && (phase & 0x1ff)) {
phase--;
neg = ~0;
phase = (phase & 0x1ff) ^ 0x1ff;
}
out = phase << 3;
return envelope_calcexp(out + (envelope << 3)) ^ neg;
}
envelope_sinfunc envelope_sin[8] = {
envelope_calcsin0,
envelope_calcsin1,
envelope_calcsin2,
envelope_calcsin3,
envelope_calcsin4,
envelope_calcsin5,
envelope_calcsin6,
envelope_calcsin7
};
void envelope_gen_off(opl_slot *slott);
void envelope_gen_change(opl_slot *slott);
void envelope_gen_attack(opl_slot *slott);
void envelope_gen_decay(opl_slot *slott);
void envelope_gen_sustain(opl_slot *slott);
void envelope_gen_release(opl_slot *slott);
envelope_genfunc envelope_gen[6] = {
envelope_gen_off,
envelope_gen_attack,
envelope_gen_decay,
envelope_gen_sustain,
envelope_gen_release,
envelope_gen_change
};
enum envelope_gen_num {
envelope_gen_num_off = 0,
envelope_gen_num_attack = 1,
envelope_gen_num_decay = 2,
envelope_gen_num_sustain = 3,
envelope_gen_num_release = 4,
envelope_gen_num_change = 5
};
Bit8u envelope_calc_rate(opl_slot *slot, Bit8u reg_rate) {
if (reg_rate == 0x00) {
return 0x00;
}
Bit8u rate = (reg_rate << 2) + (slot->reg_ksr ? slot->channel->ksv : (slot->channel->ksv >> 2));
if (rate > 0x3c) {
rate = 0x3c;
}
return rate;
}
void envelope_update_ksl(opl_slot *slot) {
Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 1) - ((slot->channel->block ^ 0x07) << 5) - 0x20;
if (ksl < 0) {
ksl = 0;
}
slot->eg_ksl = (Bit8u)ksl;
}
void envelope_update_rate(opl_slot *slot) {
switch (slot->eg_gen) {
case envelope_gen_num_off:
slot->eg_rate = 0;
break;
case envelope_gen_num_attack:
slot->eg_rate = envelope_calc_rate(slot, slot->reg_ar);
break;
case envelope_gen_num_decay:
slot->eg_rate = envelope_calc_rate(slot, slot->reg_dr);
break;
case envelope_gen_num_sustain:
case envelope_gen_num_release:
slot->eg_rate = envelope_calc_rate(slot, slot->reg_rr);
break;
}
}
void envelope_gen_off(opl_slot *slot) {
slot->eg_rout = 0x1ff;
}
void envelope_gen_change(opl_slot *slot) {
slot->eg_gen = slot->eg_gennext;
envelope_update_rate(slot);
}
void envelope_gen_attack(opl_slot *slot) {
slot->eg_rout += ((~slot->eg_rout) *slot->eg_inc) >> 3;
if (slot->eg_rout < 0x00) {
slot->eg_rout = 0x00;
}
if (!slot->eg_rout) {
slot->eg_gen = envelope_gen_num_change;
slot->eg_gennext = envelope_gen_num_decay;
}
}
void envelope_gen_decay(opl_slot *slot) {
slot->eg_rout += slot->eg_inc;
if (slot->eg_rout >= slot->reg_sl << 4) {
slot->eg_gen = envelope_gen_num_change;
slot->eg_gennext = envelope_gen_num_sustain;
}
}
void envelope_gen_sustain(opl_slot *slot) {
if (!slot->reg_type) {
envelope_gen_release(slot);
}
}
void envelope_gen_release(opl_slot *slot) {
slot->eg_rout += slot->eg_inc;
if (slot->eg_rout >= 0x1ff) {
slot->eg_gen = envelope_gen_num_change;
slot->eg_gennext = envelope_gen_num_off;
}
}
void envelope_calc(opl_slot *slot) {
Bit8u rate_h, rate_l;
rate_h = slot->eg_rate >> 2;
rate_l = slot->eg_rate & 3;
Bit8u inc = 0;
if (slot->eg_gen == envelope_gen_num_attack && rate_h == 0x0f) {
inc = 8;
}
else if (eg_incsh[rate_h] > 0) {
if ((slot->chip->timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) {
inc = eg_incstep[eg_incdesc[rate_h]][rate_l][((slot->chip->timer) >> eg_incsh[rate_h]) & 0x07];
}
}
else {
inc = eg_incstep[eg_incdesc[rate_h]][rate_l][slot->chip->timer & 0x07] << (-eg_incsh[rate_h]);
}
slot->eg_inc = inc;
envelope_gen[slot->eg_gen](slot);
slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem;
}
void eg_keyon(opl_slot *slot, Bit8u type) {
if (!slot->key) {
slot->eg_gen = envelope_gen_num_change;
slot->eg_gennext = envelope_gen_num_attack;
slot->pg_phase = 0x00;
}
slot->key |= type;
}
void eg_keyoff(opl_slot *slot, Bit8u type) {
if (slot->key) {
slot->key &= (~type);
if (!slot->key) {
slot->eg_gen = envelope_gen_num_change;
slot->eg_gennext = envelope_gen_num_release;
}
}
}
//
// Phase Generator
//
void pg_generate(opl_slot *slot) {
Bit16u f_num = slot->channel->f_num;
if (slot->reg_vib) {
Bit8u f_num_high = f_num >> (7 + vib_table[(slot->chip->timer >> 10)&0x07] + (0x01 - slot->chip->dvb));
f_num += f_num_high * vibsgn_table[(slot->chip->timer >> 10) & 0x07];
}
slot->pg_phase += (((f_num << slot->channel->block) >> 1) * mt[slot->reg_mult]) >> 1;
}
//
// Noise Generator
//
void n_generate(opl_chip *chip) {
if (chip->noise & 0x01) {
chip->noise ^= 0x800302;
}
chip->noise >>= 1;
}
//
// Slot
//
void slot_write20(opl_slot *slot,Bit8u data) {
if ((data >> 7) & 0x01) {
slot->trem = &slot->chip->tremval;
}
else {
slot->trem = (Bit8u*)&slot->chip->zeromod;
}
slot->reg_vib = (data >> 6) & 0x01;
slot->reg_type = (data >> 5) & 0x01;
slot->reg_ksr = (data >> 4) & 0x01;
slot->reg_mult = data & 0x0f;
envelope_update_rate(slot);
}
void slot_write40(opl_slot *slot, Bit8u data) {
slot->reg_ksl = (data >> 6) & 0x03;
slot->reg_tl = data & 0x3f;
envelope_update_ksl(slot);
}
void slot_write60(opl_slot *slot, Bit8u data) {
slot->reg_ar = (data >> 4) & 0x0f;
slot->reg_dr = data & 0x0f;
envelope_update_rate(slot);
}
void slot_write80(opl_slot *slot, Bit8u data) {
slot->reg_sl = (data >> 4) & 0x0f;
if (slot->reg_sl == 0x0f) {
slot->reg_sl = 0x1f;
}
slot->reg_rr = data & 0x0f;
envelope_update_rate(slot);
}
void slot_writee0(opl_slot *slot, Bit8u data) {
slot->reg_wf = data & 0x07;
if (slot->chip->newm == 0x00) {
slot->reg_wf &= 0x03;
}
}
void slot_generatephase(opl_slot *slot, Bit16u phase) {
slot->out = envelope_sin[slot->reg_wf](phase, slot->eg_out);
}
void slot_generate(opl_slot *slot) {
slot->out = envelope_sin[slot->reg_wf]((slot->pg_phase >> 9) + (*slot->mod), slot->eg_out);
}
void slot_generatezm(opl_slot *slot) {
slot->out = envelope_sin[slot->reg_wf]((slot->pg_phase >> 9), slot->eg_out);
}
void slot_calgfb(opl_slot *slot) {
slot->prout[1] = slot->prout[0];
slot->prout[0] = slot->out;
if (slot->channel->fb != 0x00) {
slot->fbmod = (slot->prout[0] + slot->prout[1]) >> (0x09 - slot->channel->fb);
}
else {
slot->fbmod = 0;
}
}
//
// Channel
//
void chan_setupalg(opl_channel *channel);
void chan_updaterhythm(opl_chip *chip, Bit8u data) {
chip->rhy = data & 0x3f;
if (chip->rhy & 0x20) {
chip->channel[6].out[0] = &chip->slot[13].out;
chip->channel[6].out[1] = &chip->slot[13].out;
chip->channel[6].out[2] = &chip->zeromod;
chip->channel[6].out[3] = &chip->zeromod;
chip->channel[7].out[0] = &chip->slot[14].out;
chip->channel[7].out[1] = &chip->slot[14].out;
chip->channel[7].out[2] = &chip->slot[15].out;
chip->channel[7].out[3] = &chip->slot[15].out;
chip->channel[8].out[0] = &chip->slot[16].out;
chip->channel[8].out[1] = &chip->slot[16].out;
chip->channel[8].out[2] = &chip->slot[17].out;
chip->channel[8].out[3] = &chip->slot[17].out;
for (Bit8u chnum = 6; chnum < 9; chnum++) {
chip->channel[chnum].chtype = ch_drum;
}
//hh
if (chip->rhy & 0x01) {
eg_keyon(&chip->slot[14], egk_drum);
}
else {
eg_keyoff(&chip->slot[14], egk_drum);
}
//tc
if (chip->rhy & 0x02) {
eg_keyon(&chip->slot[17], egk_drum);
}
else {
eg_keyoff(&chip->slot[17], egk_drum);
}
//tom
if (chip->rhy & 0x04) {
eg_keyon(&chip->slot[16], egk_drum);
}
else {
eg_keyoff(&chip->slot[16], egk_drum);
}
//sd
if (chip->rhy & 0x08) {
eg_keyon(&chip->slot[15], egk_drum);
}
else {
eg_keyoff(&chip->slot[15], egk_drum);
}
//bd
if (chip->rhy & 0x10) {
eg_keyon(&chip->slot[12], egk_drum);
eg_keyon(&chip->slot[13], egk_drum);
}
else {
eg_keyoff(&chip->slot[12], egk_drum);
eg_keyoff(&chip->slot[13], egk_drum);
}
}
else {
for (Bit8u chnum = 6; chnum < 9; chnum++) {
chip->channel[chnum].chtype = ch_2op;
chan_setupalg(&chip->channel[chnum]);
}
}
}
void chan_writea0(opl_channel *channel, Bit8u data) {
if (channel->chip->newm && channel->chtype == ch_4op2) {
return;
}
channel->f_num = (channel->f_num & 0x300) | data;
channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01);
envelope_update_ksl(channel->slots[0]);
envelope_update_ksl(channel->slots[1]);
envelope_update_rate(channel->slots[0]);
envelope_update_rate(channel->slots[1]);
if (channel->chip->newm && channel->chtype == ch_4op) {
channel->pair->f_num = channel->f_num;
channel->pair->ksv = channel->ksv;
envelope_update_ksl(channel->pair->slots[0]);
envelope_update_ksl(channel->pair->slots[1]);
envelope_update_rate(channel->pair->slots[0]);
envelope_update_rate(channel->pair->slots[1]);
}
}
void chan_writeb0(opl_channel *channel, Bit8u data) {
if (channel->chip->newm && channel->chtype == ch_4op2) {
return;
}
channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8);
channel->block = (data >> 2) & 0x07;
channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01);
envelope_update_ksl(channel->slots[0]);
envelope_update_ksl(channel->slots[1]);
envelope_update_rate(channel->slots[0]);
envelope_update_rate(channel->slots[1]);
if (channel->chip->newm && channel->chtype == ch_4op) {
channel->pair->f_num = channel->f_num;
channel->pair->block = channel->block;
channel->pair->ksv = channel->ksv;
envelope_update_ksl(channel->pair->slots[0]);
envelope_update_ksl(channel->pair->slots[1]);
envelope_update_rate(channel->pair->slots[0]);
envelope_update_rate(channel->pair->slots[1]);
}
}
void chan_setupalg(opl_channel *channel) {
if (channel->chtype == ch_drum) {
return;
}
if (channel->alg & 0x08) {
return;
}
if (channel->alg & 0x04) {
channel->pair->out[0] = &channel->chip->zeromod;
channel->pair->out[1] = &channel->chip->zeromod;
channel->pair->out[2] = &channel->chip->zeromod;
channel->pair->out[3] = &channel->chip->zeromod;
switch (channel->alg & 0x03) {
case 0x00:
channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
channel->pair->slots[1]->mod = &channel->pair->slots[0]->out;
channel->slots[0]->mod = &channel->pair->slots[1]->out;
channel->slots[1]->mod = &channel->slots[0]->out;
channel->out[0] = &channel->slots[1]->out;
channel->out[1] = &channel->chip->zeromod;
channel->out[2] = &channel->chip->zeromod;
channel->out[3] = &channel->chip->zeromod;
break;
case 0x01:
channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
channel->pair->slots[1]->mod = &channel->pair->slots[0]->out;
channel->slots[0]->mod = &channel->chip->zeromod;
channel->slots[1]->mod = &channel->slots[0]->out;
channel->out[0] = &channel->pair->slots[1]->out;
channel->out[1] = &channel->slots[1]->out;
channel->out[2] = &channel->chip->zeromod;
channel->out[3] = &channel->chip->zeromod;
break;
case 0x02:
channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
channel->pair->slots[1]->mod = &channel->chip->zeromod;
channel->slots[0]->mod = &channel->pair->slots[1]->out;
channel->slots[1]->mod = &channel->slots[0]->out;
channel->out[0] = &channel->pair->slots[0]->out;
channel->out[1] = &channel->slots[1]->out;
channel->out[2] = &channel->chip->zeromod;
channel->out[3] = &channel->chip->zeromod;
break;
case 0x03:
channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod;
channel->pair->slots[1]->mod = &channel->chip->zeromod;
channel->slots[0]->mod = &channel->pair->slots[1]->out;
channel->slots[1]->mod = &channel->chip->zeromod;
channel->out[0] = &channel->pair->slots[0]->out;
channel->out[1] = &channel->slots[0]->out;
channel->out[2] = &channel->slots[1]->out;
channel->out[3] = &channel->chip->zeromod;
break;
}
}
else {
switch (channel->alg & 0x01) {
case 0x00:
channel->slots[0]->mod = &channel->slots[0]->fbmod;
channel->slots[1]->mod = &channel->slots[0]->out;
channel->out[0] = &channel->slots[1]->out;
channel->out[1] = &channel->chip->zeromod;
channel->out[2] = &channel->chip->zeromod;
channel->out[3] = &channel->chip->zeromod;
break;
case 0x01:
channel->slots[0]->mod = &channel->slots[0]->fbmod;
channel->slots[1]->mod = &channel->chip->zeromod;
channel->out[0] = &channel->slots[0]->out;
channel->out[1] = &channel->slots[1]->out;
channel->out[2] = &channel->chip->zeromod;
channel->out[3] = &channel->chip->zeromod;
break;
}
}
}
void chan_writec0(opl_channel *channel, Bit8u data) {
channel->fb = (data & 0x0e) >> 1;
channel->con = data & 0x01;
channel->alg = channel->con;
if (channel->chip->newm) {
if (channel->chtype == ch_4op) {
channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con);
channel->alg = 0x08;
chan_setupalg(channel->pair);
}
else if (channel->chtype == ch_4op2) {
channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con);
channel->pair->alg = 0x08;
chan_setupalg(channel);
}
else {
chan_setupalg(channel);
}
}
else {
chan_setupalg(channel);
}
if (channel->chip->newm) {
channel->cha = ((data >> 4) & 0x01) ? ~0 : 0;
channel->chb = ((data >> 5) & 0x01) ? ~0 : 0;
}
else {
channel->cha = channel->chb = ~0;
}
}
void chan_generaterhythm(opl_chip *chip) {
if (chip->rhy & 0x20) {
opl_channel *channel6 = &chip->channel[6];
opl_channel *channel7 = &chip->channel[7];
opl_channel *channel8 = &chip->channel[8];
slot_generate(channel6->slots[0]);
slot_generate(channel6->slots[1]);
Bit16u phase14 = channel7->slots[0]->pg_phase & 0x3ff;
Bit16u phase17 = channel8->slots[1]->pg_phase & 0x3ff;
Bit16u phase = 0x00;
//hh tc phase bit
Bit16u phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00;
//hh
phase = (phasebit << 9) | (0x34 << ((phasebit ^ (chip->noise & 0x01) << 1)));
slot_generatephase(channel7->slots[0], phase);
//sd
phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8);
slot_generatephase(channel7->slots[1], phase);
//tt
slot_generatezm(channel8->slots[0]);
//tc
phase = 0x100 | (phasebit << 9);
slot_generatephase(channel8->slots[1], phase);
}
}
void chan_generate(opl_channel *channel) {
if (channel->chtype == ch_drum) {
return;
}
if (channel->alg & 0x08) {
return;
}
if (channel->alg & 0x04) {
slot_generate(channel->pair->slots[0]);
slot_generate(channel->pair->slots[1]);
slot_generate(channel->slots[0]);
slot_generate(channel->slots[1]);
}
else {
slot_generate(channel->slots[0]);
slot_generate(channel->slots[1]);
}
}
void chan_enable(opl_channel *channel) {
if (channel->chip->newm) {
if (channel->chtype == ch_4op) {
eg_keyon(channel->slots[0], egk_norm);
eg_keyon(channel->slots[1], egk_norm);
eg_keyon(channel->pair->slots[0], egk_norm);
eg_keyon(channel->pair->slots[1], egk_norm);
}
else if (channel->chtype == ch_2op || channel->chtype == ch_drum) {
eg_keyon(channel->slots[0], egk_norm);
eg_keyon(channel->slots[1], egk_norm);
}
}
else {
eg_keyon(channel->slots[0], egk_norm);
eg_keyon(channel->slots[1], egk_norm);
}
}
void chan_disable(opl_channel *channel) {
if (channel->chip->newm) {
if (channel->chtype == ch_4op) {
eg_keyoff(channel->slots[0], egk_norm);
eg_keyoff(channel->slots[1], egk_norm);
eg_keyoff(channel->pair->slots[0], egk_norm);
eg_keyoff(channel->pair->slots[1], egk_norm);
}
else if (channel->chtype == ch_2op || channel->chtype == ch_drum) {
eg_keyoff(channel->slots[0], egk_norm);
eg_keyoff(channel->slots[1], egk_norm);
}
}
else {
eg_keyoff(channel->slots[0], egk_norm);
eg_keyoff(channel->slots[1], egk_norm);
}
}
void chan_set4op(opl_chip *chip, Bit8u data) {
for (Bit8u bit = 0; bit < 6; bit++) {
Bit8u chnum = bit;
if (bit >= 3) {
chnum += 9 - 3;
}
if ((data >> bit) & 0x01) {
chip->channel[chnum].chtype = ch_4op;
chip->channel[chnum + 3].chtype = ch_4op2;
}
else {
chip->channel[chnum].chtype = ch_2op;
chip->channel[chnum + 3].chtype = ch_2op;
}
}
}
Bit16s limshort(Bit32s a) {
if (a > 32767) {
a = 32767;
}
else if (a < -32768) {
a = -32768;
}
return (Bit16s)a;
}
void NukedOPL3::Reset() {
memset(&opl3, 0, sizeof(opl_chip));
for (Bit8u slotnum = 0; slotnum < 36; slotnum++) {
opl3.slot[slotnum].channel = &opl3.channel[slotnum / 2];
opl3.slot[slotnum].chip = &opl3;
opl3.slot[slotnum].mod = &opl3.zeromod;
opl3.slot[slotnum].eg_rout = 0x1ff;
opl3.slot[slotnum].eg_out = 0x1ff;
opl3.slot[slotnum].eg_gen = envelope_gen_num_off;
opl3.slot[slotnum].eg_gennext = envelope_gen_num_off;
opl3.slot[slotnum].trem = (Bit8u*)&opl3.zeromod;
}
for (Bit8u channum = 0; channum < 18; channum++) {
opl3.channel[channum].slots[0] = &opl3.slot[2 * channum];
opl3.channel[channum].slots[1] = &opl3.slot[2 * channum + 1];
if ((channum % 9) < 3) {
opl3.channel[channum].pair = &opl3.channel[channum + 3];
}
else if ((channum % 9) < 6) {
opl3.channel[channum].pair = &opl3.channel[channum - 3];
}
opl3.channel[channum].chip = &opl3;
opl3.channel[channum].out[0] = &opl3.zeromod;
opl3.channel[channum].out[1] = &opl3.zeromod;
opl3.channel[channum].out[2] = &opl3.zeromod;
opl3.channel[channum].out[3] = &opl3.zeromod;
opl3.channel[channum].chtype = ch_2op;
opl3.channel[channum].cha = ~0;
opl3.channel[channum].chb = ~0;
opl3.channel[channum].fcha = 1.0;
opl3.channel[channum].fchb = 1.0;
chan_setupalg(&opl3.channel[channum]);
}
opl3.noise = 0x306600;
}
void NukedOPL3::WriteReg(int reg, int v) {
v &= 0xff;
reg &= 0x1ff;
Bit8u high = (reg >> 8) & 0x01;
Bit8u regm = reg & 0xff;
switch (regm & 0xf0) {
case 0x00:
if (high) {
switch (regm & 0x0f) {
case 0x04:
chan_set4op(&opl3, v);
break;
case 0x05:
opl3.newm = v & 0x01;
break;
}
}
else {
switch (regm & 0x0f) {
case 0x08:
opl3.nts = (v >> 6) & 0x01;
break;
}
}
break;
case 0x20:
case 0x30:
if (ad_slot[regm & 0x1f] >= 0) {
slot_write20(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);
}
break;
case 0x40:
case 0x50:
if (ad_slot[regm & 0x1f] >= 0) {
slot_write40(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);
}
break;
case 0x60:
case 0x70:
if (ad_slot[regm & 0x1f] >= 0) {
slot_write60(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);
}
break;
case 0x80:
case 0x90:
if (ad_slot[regm & 0x1f] >= 0) {
slot_write80(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);;
}
break;
case 0xe0:
case 0xf0:
if (ad_slot[regm & 0x1f] >= 0) {
slot_writee0(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);
}
break;
case 0xa0:
if ((regm & 0x0f) < 9) {
chan_writea0(&opl3.channel[9 * high + (regm & 0x0f)], v);
}
break;
case 0xb0:
if (regm == 0xbd && !high) {
opl3.dam = v >> 7;
opl3.dvb = (v >> 6) & 0x01;
chan_updaterhythm(&opl3, v);
}
else if ((regm & 0x0f) < 9) {
chan_writeb0(&opl3.channel[9 * high + (regm & 0x0f)], v);
if (v & 0x20) {
chan_enable(&opl3.channel[9 * high + (regm & 0x0f)]);
}
else {
chan_disable(&opl3.channel[9 * high + (regm & 0x0f)]);
}
}
break;
case 0xc0:
if ((regm & 0x0f) < 9) {
chan_writec0(&opl3.channel[9 * high + (regm & 0x0f)], v);
}
break;
}
}
void NukedOPL3::Update(float* sndptr, int numsamples) {
Bit32s outa, outb;
for (Bit32u i = 0; i < (Bit32u)numsamples; i++) {
outa = 0;
outb = 0;
for (Bit8u ii = 0; ii < 36; ii++) {
slot_calgfb(&opl3.slot[ii]);
}
chan_generaterhythm(&opl3);
for (Bit8u ii = 0; ii < 18; ii++) {
chan_generate(&opl3.channel[ii]);
Bit16s accm = 0;
for (Bit8u jj = 0; jj < 4; jj++) {
accm += *opl3.channel[ii].out[jj];
}
if (FullPan) {
outa += (Bit16s)(accm * opl3.channel[ii].fcha);
outb += (Bit16s)(accm * opl3.channel[ii].fchb);
}
else {
outa += (Bit16s)(accm & opl3.channel[ii].cha);
outb += (Bit16s)(accm & opl3.channel[ii].chb);
}
}
for (Bit8u ii = 0; ii < 36; ii++) {
envelope_calc(&opl3.slot[ii]);
pg_generate(&opl3.slot[ii]);
}
n_generate(&opl3);
opl3.timer++;
if (!(opl3.timer & 0x3f)) {
if (!opl3.tremdir) {
if (opl3.tremtval == 105) {
opl3.tremtval--;
opl3.tremdir = 1;
}
else {
opl3.tremtval++;
}
}
else {
if (opl3.tremtval == 0) {
opl3.tremtval++;
opl3.tremdir = 0;
}
else {
opl3.tremtval--;
}
}
opl3.tremval = (opl3.tremtval >> 2) >> ((1 - opl3.dam) << 1);
}
*sndptr++ += (float)(outa / 10240.0);
*sndptr++ += (float)(outb / 10240.0);
}
}
void NukedOPL3::SetPanning(int c, float left, float right) {
if (FullPan) {
opl3.channel[c].fcha = left;
opl3.channel[c].fchb = right;
}
}
NukedOPL3::NukedOPL3(bool stereo) {
FullPan = stereo;
Reset();
}
OPLEmul *NukedOPL3Create(bool stereo) {
return new NukedOPL3(stereo);
}

234
src/oplsynth/nukedopl3.h Normal file
View File

@ -0,0 +1,234 @@
/*
* Copyright (C) 2013-2014 Nuke.YKT
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
Nuked Yamaha YMF262(aka OPL3) emulator.
Thanks:
MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh):
Feedback and Rhythm part calculation information.
forums.submarine.org.uk(carbon14, opl3):
Tremolo and phase generator calculation information.
OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
OPL2 ROMs.
*/
//version 1.5
#include "opl.h"
#include "muslib.h"
typedef uintptr_t Bitu;
typedef intptr_t Bits;
typedef DWORD Bit32u;
typedef SDWORD Bit32s;
typedef WORD Bit16u;
typedef SWORD Bit16s;
typedef BYTE Bit8u;
typedef SBYTE Bit8s;
// Channel types
enum {
ch_2op = 0,
ch_4op = 1,
ch_4op2 = 2,
ch_drum = 3
};
// Envelope key types
enum {
egk_norm = 0x01,
egk_drum = 0x02
};
//
// logsin table
//
static const Bit16u logsinrom[256] = {
0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365,
0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261,
0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd,
0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166,
0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118,
0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db,
0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9,
0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081,
0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060,
0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045,
0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f,
0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e,
0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011,
0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007,
0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002,
0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000
};
//
// exp table
//
static const Bit16u exprom[256] = {
0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a,
0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a,
0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b,
0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be,
0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4,
0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c,
0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167,
0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4,
0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4,
0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227,
0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d,
0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5,
0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302,
0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351,
0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4,
0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa
};
//
// freq mult table multiplied by 2
//
// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15
//
static const Bit8u mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 };
//
// ksl table
//
static const Bit8u kslrom[16] = { 0, 64, 80, 90, 96, 102, 106, 110, 112, 116, 118, 120, 122, 124, 126, 127 };
static const Bit8u kslshift[4] = { 8, 1, 2, 0 };
//
// LFO vibrato
//
static const Bit8u vib_table[8] = { 3, 1, 0, 1, 3, 1, 0, 1 };
static const Bit8s vibsgn_table[8] = { 1, 1, 1, 1, -1, -1, -1, -1 };
//
// envelope generator constants
//
static const Bit8u eg_incstep[3][4][8] = {
{ { 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, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 1, 1, 0, 1 }, { 1, 1, 1, 1, 1, 1, 0, 1 } },
{ { 1, 1, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 2, 2, 1, 1 }, { 2, 2, 2, 2, 2, 2, 1, 1 } }
};
static const Bit8u eg_incdesc[16] = {
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2
};
static const Bit8s eg_incsh[16] = {
0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2
};
//
// address decoding
//
static const Bit8s ad_slot[0x20] = { 0, 2, 4, 1, 3, 5, -1, -1, 6, 8, 10, 7, 9, 11, -1, -1, 12, 14, 16, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
struct opl_chip;
struct opl_slot;
struct opl_channel;
struct opl_slot {
opl_channel *channel;
opl_chip *chip;
Bit16s out;
Bit16s fbmod;
Bit16s *mod;
Bit16s prout[2];
Bit16s eg_rout;
Bit16s eg_out;
Bit8u eg_inc;
Bit8u eg_gen;
Bit8u eg_gennext;
Bit8u eg_rate;
Bit8u eg_ksl;
Bit8u *trem;
Bit8u reg_vib;
Bit8u reg_type;
Bit8u reg_ksr;
Bit8u reg_mult;
Bit8u reg_ksl;
Bit8u reg_tl;
Bit8u reg_ar;
Bit8u reg_dr;
Bit8u reg_sl;
Bit8u reg_rr;
Bit8u reg_wf;
Bit8u key;
Bit32u pg_phase;
};
struct opl_channel {
opl_slot *slots[2];
opl_channel *pair;
opl_chip *chip;
Bit16s *out[4];
Bit8u chtype;
Bit16u f_num;
Bit8u block;
Bit8u fb;
Bit8u con;
Bit8u alg;
Bit8u ksv;
Bit16u cha, chb;
float fcha, fchb;
};
struct opl_chip {
opl_channel channel[18];
opl_slot slot[36];
Bit16u timer;
Bit8u newm;
Bit8u nts;
Bit8u dvb;
Bit8u dam;
Bit8u rhy;
Bit8u vibpos;
Bit8u tremval;
Bit8u tremtval;
Bit8u tremdir;
Bit32u noise;
Bit16s zeromod;
};
class NukedOPL3 : public OPLEmul {
private:
opl_chip opl3;
bool FullPan;
public:
void Reset();
void Update(float* sndptr, int numsamples);
void WriteReg(int reg, int v);
void SetPanning(int c, float left, float right);
NukedOPL3(bool stereo);
};

View File

@ -20,6 +20,7 @@ public:
OPLEmul *YM3812Create(bool stereo);
OPLEmul *DBOPLCreate(bool stereo);
OPLEmul *JavaOPLCreate(bool stereo);
OPLEmul *NukedOPL3Create(bool stereo);
#define OPL_SAMPLE_RATE 49716.0
#define CENTER_PANNING_POWER 0.70710678118 /* [RH] volume at center for EQP */

View File

@ -120,6 +120,7 @@ static void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flag
//Add the floor
ffloor = new F3DFloor;
ffloor->top.copied = ffloor->bottom.copied = false;
ffloor->top.model = ffloor->bottom.model = ffloor->model = sec2;
ffloor->target = sec;
ffloor->ceilingclip = ffloor->floorclip = NULL;
@ -420,6 +421,8 @@ void P_Recalculate3DFloors(sector_t * sector)
F3DFloor * pick;
unsigned pickindex;
F3DFloor * clipped=NULL;
F3DFloor * solid=NULL;
fixed_t solid_bottom=0;
fixed_t clipped_top;
fixed_t clipped_bottom=0;
fixed_t maxheight, minheight;
@ -477,6 +480,7 @@ void P_Recalculate3DFloors(sector_t * sector)
}
oldlist.Delete(pickindex);
fixed_t pick_bottom=pick->bottom.plane->ZatPoint(CenterSpot(sector));
if (pick->flags & FF_THISINSIDE)
{
@ -486,10 +490,38 @@ void P_Recalculate3DFloors(sector_t * sector)
}
else if (pick->flags&(FF_SWIMMABLE|FF_TRANSLUCENT) && pick->flags&FF_EXISTS)
{
clipped=pick;
clipped_top=height;
clipped_bottom=pick->bottom.plane->ZatPoint(CenterSpot(sector));
ffloors.Push(pick);
// We must check if this nonsolid segment gets clipped from the top by another 3D floor
if (solid != NULL && solid_bottom < height)
{
ffloors.Push(pick);
if (solid_bottom < pick_bottom)
{
// this one is fully covered
pick->flags|=FF_CLIPPED;
pick->flags&=~FF_EXISTS;
}
else
{
F3DFloor * dyn=new F3DFloor;
*dyn=*pick;
pick->flags|=FF_CLIPPED;
pick->flags&=~FF_EXISTS;
dyn->flags|=FF_DYNAMIC;
dyn->top.copyPlane(&solid->bottom);
ffloors.Push(dyn);
clipped = dyn;
clipped_top = solid_bottom;
clipped_bottom = pick_bottom;
}
}
else
{
clipped = pick;
clipped_top = height;
clipped_bottom = pick_bottom;
ffloors.Push(pick);
}
}
else if (clipped && clipped_bottom<height)
{
@ -499,12 +531,10 @@ void P_Recalculate3DFloors(sector_t * sector)
clipped->flags|=FF_CLIPPED;
clipped->flags&=~FF_EXISTS;
dyn->flags|=FF_DYNAMIC;
dyn->bottom=pick->top;
dyn->bottom.copyPlane(&pick->top);
ffloors.Push(dyn);
ffloors.Push(pick);
fixed_t pick_bottom=pick->bottom.plane->ZatPoint(CenterSpot(sector));
if (pick_bottom<=clipped_bottom)
{
clipped=NULL;
@ -515,14 +545,25 @@ void P_Recalculate3DFloors(sector_t * sector)
dyn=new F3DFloor;
*dyn=*clipped;
dyn->flags|=FF_DYNAMIC|FF_EXISTS;
dyn->top=pick->bottom;
dyn->top.copyPlane(&pick->bottom);
ffloors.Push(dyn);
clipped = dyn;
clipped_top = pick_bottom;
}
solid = pick;
solid_bottom = pick_bottom;
}
else
{
clipped=NULL;
clipped = NULL;
if (solid == NULL || solid_bottom > pick_bottom)
{
// only if this one is lower
solid = pick;
solid_bottom = pick_bottom;
}
ffloors.Push(pick);
}
}
@ -910,3 +951,27 @@ int P_Find3DFloor(sector_t * sec, fixed_t x, fixed_t y, fixed_t z, bool above, b
}
#endif
#include "c_dispatch.h"
CCMD (dump3df)
{
if (argv.argc() > 1)
{
int sec = strtol(argv[1], NULL, 10);
sector_t *sector = &sectors[sec];
TArray<F3DFloor*> & ffloors=sector->e->XFloor.ffloors;
for (unsigned int i = 0; i < ffloors.Size(); i++)
{
fixed_t height=ffloors[i]->top.plane->ZatPoint(CenterSpot(sector));
fixed_t bheight=ffloors[i]->bottom.plane->ZatPoint(CenterSpot(sector));
Printf("FFloor %d @ top = %f (model = %d), bottom = %f (model = %d), flags = %B, alpha = %d %s %s\n",
i, height / 65536., ffloors[i]->top.model->sectornum,
bheight / 65536., ffloors[i]->bottom.model->sectornum,
ffloors[i]->flags, ffloors[i]->alpha, (ffloors[i]->flags&FF_EXISTS)? "Exists":"", (ffloors[i]->flags&FF_DYNAMIC)? "Dynamic":"");
}
}
}

Some files were not shown because too many files have changed in this diff Show More