This is a comparison of data structures for game entities (aka objects, actors...) across different games and game engines.
- Source code has to be at least partly available, either in original form or through reverse-engineering or a remake
- The complexity of a 3D game makes those generally more interesting
- Similarly, a game in the action genre has more interesting architectural challenges than other genres - complex AI, several different object behaviours, all data structures must be highly performant, etc.
- The code is preferentially in the C/C++ languages
- The code is understandable - this excluded the Duke Nukem 3D source code - even though the standard is low
The questions below are I think the most relevant ones which can be answered by looking at game code. They explain the architecture of a game engine.
- Is there an entity "factory", returning an entity given an id or string?
- How is an entity is spawned? Example: a projectile. How is it removed?
- How is the model or sprite of an entity set? How are animations set?
- How is the collider of an entity set? How can you check for collision against entities?
- How does an entity play a sound? How does the engine update sound position, volume, etc.?
- How does an entity move, while handling collisions with the level?
- How do the entities keep references to each other? Pointers, ids? Example: target chased by an enemy
- How is the think/update function defined?
Eventually, I'd like to answer each of those questions for each of the engines mentioned below.
- Doom aka idTech1
- Blood aka Build engine
- Quake aka idTech2
- Quake II aka idTech2.5
- Quake III aka idTech3
- GoldSrc
- LithTech2
- The Legend of Zelda: Ocarina of Time
- Super Mario 64
- Cube 2: Sauerbraten
- Doukutsu Monogatari aka Cave Story
- STALKER aka XRay engine
- GTA III
- OpenMW
- Diablo
- Starcraft
- Freespace / Freespace 2
// Map Object definition.
typedef struct mobj_s
{
// List: thinker links.
thinker_t thinker;
// Info for drawing: position.
fixed_t x;
fixed_t y;
fixed_t z;
// More list: links in sector (if needed)
struct mobj_s* snext;
struct mobj_s* sprev;
//More drawing info: to determine current sprite.
angle_t angle; // orientation
spritenum_t sprite; // used to find patch_t and flip value
int frame; // might be ORed with FF_FULLBRIGHT
// Interaction info, by BLOCKMAP.
// Links in blocks (if needed).
struct mobj_s* bnext;
struct mobj_s* bprev;
struct subsector_s* subsector;
// The closest interval over all contacted Sectors.
fixed_t floorz;
fixed_t ceilingz;
// For movement checking.
fixed_t radius;
fixed_t height;
// Momentums, used to update position.
fixed_t momx;
fixed_t momy;
fixed_t momz;
// If == validcount, already checked.
int validcount;
mobjtype_t type;
mobjinfo_t* info; // &mobjinfo[mobj->type]
int tics; // state tic counter
state_t* state;
int flags;
int health;
// Movement direction, movement generation (zig-zagging).
int movedir; // 0-7
int movecount; // when 0, select a new dir
// Thing being chased/attacked (or NULL),
// also the originator for missiles.
struct mobj_s* target;
// Reaction time: if non 0, don't attack yet.
// Used by player to freeze a bit after teleporting.
int reactiontime;
// If >0, the target will be chased
// no matter what (even if shot)
int threshold;
// Additional info record for player avatars only.
// Only valid if type == MT_PLAYER
struct player_s* player;
// Player number last looked for.
int lastlook;
// For nightmare respawn.
mapthing_t spawnpoint;
// Thing being chased/attacked for tracers.
struct mobj_s* tracer;
} mobj_t;
// Doubly linked list of actors.
typedef struct thinker_s
{
struct thinker_s* prev;
struct thinker_s* next;
think_t function;
} thinker_t;
struct XSPRITE
{
signed reference : 14;
unsigned state : 1;
// trigger data
unsigned busy : 17;
// 0 bits unused
unsigned txID : 10;
unsigned rxID : 10;
unsigned command : 8;
unsigned triggerOn : 1;
unsigned triggerOff : 1;
// 2 bits unused
unsigned busyTime : 12; // time to reach next state
unsigned waitTime : 12; // delay before callback
unsigned restState : 1; // state to return to on callback
unsigned interruptable : 1;
unsigned difficulty : 2;
// 4 bits unused
unsigned soundId : 8;
// physical triggers
unsigned decoupled : 1;
unsigned triggerOnce : 1;
unsigned isTriggered : 1; // used for triggerOnce objects
unsigned key : 3;
unsigned triggerPush : 1; // action key
unsigned triggerImpact : 1; // vector hits
unsigned triggerReserved0 : 1;
unsigned triggerPickup : 1; // secrets
unsigned triggerTouch : 1; // sawblades, spikes, zaps?
unsigned triggerSight : 1; // dunno, yet.
unsigned triggerProximity : 1; // proximity bombs
unsigned triggerReserved1 : 1;
unsigned triggerReserved2 : 1;
// 9 bits unused
signed data1 : 16; // combo value?
signed data2 : 16; // combo key?
// 0 bits unused
signed data3 : 16; // combo max?
unsigned goalAng : 11; // dudes
signed dodgeDir : 2; // dudes
unsigned locked: 1;
unsigned unused0 : 2; // USE ME!
// 0 bits unused
unsigned respawn : 2; // 0=optional never, 1=optional always, 2=always, 3=never
unsigned respawnTime : 12; // 0=permanent, >0=time in tenths of a second,
unsigned launchMode : 2; // 0=all, 1=bloodbath, 2=ally, 3=ally&bloodbath,
// this stuff needed for dudes
unsigned moveState : 8; // same as player move states
unsigned unused1 : 8; // USE ME!
// 0 bits unused
unsigned health : 12;
unsigned dudeDeaf : 1; // can't hear players
unsigned dudeAmbush : 1; // must be triggered manually
unsigned dudeGuard : 1; // won't leave sector
unsigned dudeFlag4 : 1;
signed target : 16;
// 0 bits unused
signed targetX : 32;
signed targetY : 32;
signed targetZ : 32;
unsigned burnTime : 16;
signed burnSource : 16;
unsigned unused2 : 16;
unsigned stateTimer : 16;
AISTATE *aiState;
};
struct AISTATE
{
int seqId;
SEQCALLBACK seqCallback;
int ticks;
AISTATEFUNC enter;
AISTATEFUNC move;
AISTATEFUNC think;
AISTATE *next;
};
QuakeC links
typedef struct
{
vec3_t origin;
vec3_t angles;
int modelindex;
int frame;
int colormap;
int skin;
int effects;
} entity_state_t;
typedef struct edict_s
{
qboolean free;
link_t area; // linked to a division node or leaf
int num_leafs;
short leafnums[MAX_ENT_LEAFS];
entity_state_t baseline;
float freetime; // sv.time when the object was freed
entvars_t v; // C exported fields from progs
// other fields from progs come immediately after
} edict_t;
typedef struct
{
float modelindex;
vec3_t absmin;
vec3_t absmax;
float ltime;
float movetype;
float solid;
vec3_t origin;
vec3_t oldorigin;
vec3_t velocity;
vec3_t angles;
vec3_t avelocity;
vec3_t punchangle;
string_t classname;
string_t model;
float frame;
float skin;
float effects;
vec3_t mins;
vec3_t maxs;
vec3_t size;
func_t touch;
func_t use;
func_t think;
func_t blocked;
float nextthink;
int groundentity;
float health;
float frags;
float weapon;
string_t weaponmodel;
float weaponframe;
float currentammo;
float ammo_shells;
float ammo_nails;
float ammo_rockets;
float ammo_cells;
float items;
float takedamage;
int chain;
float deadflag;
vec3_t view_ofs;
float button0;
float button1;
float button2;
float impulse;
float fixangle;
vec3_t v_angle;
float idealpitch;
string_t netname;
int enemy;
float flags;
float colormap;
float team;
float max_health;
float teleport_time;
float armortype;
float armorvalue;
float waterlevel;
float watertype;
float ideal_yaw;
float yaw_speed;
int aiment;
int goalentity;
float spawnflags;
string_t target;
string_t targetname;
float dmg_take;
float dmg_save;
int dmg_inflictor;
int owner;
vec3_t movedir;
string_t message;
float sounds;
string_t noise;
string_t noise1;
string_t noise2;
string_t noise3;
} entvars_t;
QuakeC Source for generated entvars_t
/*
==============================================================================
SOURCE FOR ENTVARS_T C STRUCTURE
==============================================================================
*/
//
// system fields (*** = do not set in prog code, maintained by C code)
//
.float modelindex; // *** model index in the precached list
.vector absmin, absmax; // *** origin + mins / maxs
.float ltime; // local time for entity
.float lastruntime; // *** to allow entities to run out of sequence
.float movetype;
.float solid;
.vector origin; // ***
.vector oldorigin; // ***
.vector velocity;
.vector angles;
.vector avelocity;
.string classname; // spawn function
.string model;
.float frame;
.float skin;
.float effects;
.vector mins, maxs; // bounding box extents reletive to origin
.vector size; // maxs - mins
.void() touch;
.void() use;
.void() think;
.void() blocked; // for doors or plats, called when can't push other
.float nextthink;
.entity groundentity;
// stats
.float health;
.float frags;
.float weapon; // one of the IT_SHOTGUN, etc flags
.string weaponmodel;
.float weaponframe;
.float currentammo;
.float ammo_shells, ammo_nails, ammo_rockets, ammo_cells;
.float items; // bit flags
.float takedamage;
.entity chain;
.float deadflag;
.vector view_ofs; // add to origin to get eye point
.float button0; // fire
.float button1; // use
.float button2; // jump
.float impulse; // weapon changes
.float fixangle;
.vector v_angle; // view / targeting angle for players
.string netname;
.entity enemy;
.float flags;
.float colormap;
.float team;
.float max_health; // players maximum health is stored here
.float teleport_time; // don't back up
.float armortype; // save this fraction of incoming damage
.float armorvalue;
.float waterlevel; // 0 = not in, 1 = feet, 2 = wast, 3 = eyes
.float watertype; // a contents value
.float ideal_yaw;
.float yaw_speed;
.entity aiment;
.entity goalentity; // a movetarget or an enemy
.float spawnflags;
.string target;
.string targetname;
// damage is accumulated through a frame. and sent as one single
// message, so the super shotgun doesn't generate huge messages
.float dmg_take;
.float dmg_save;
.entity dmg_inflictor;
.entity owner; // who launched a missile
.vector movedir; // mostly for doors, but also used for waterjump
.string message; // trigger messages
.float sounds; // either a cd track number or sound number
.string noise, noise1, noise2, noise3; // contains names of wavs to play
//================================================
void end_sys_fields; // flag for structure dumping
//================================================
// entity_state_t is the information conveyed from the server
// in an update message about entities that the client will
// need to render in some way
typedef struct entity_state_s
{
int number; // edict index
vec3_t origin;
vec3_t angles;
vec3_t old_origin; // for lerping
int modelindex;
int modelindex2, modelindex3, modelindex4; // weapons, CTF flags, etc
int frame;
int skinnum;
unsigned int effects; // PGM - we're filling it, so it needs to be unsigned
int renderfx;
int solid; // for client side prediction, 8*(bits 0-4) is x/y radius
// 8*(bits 5-9) is z down distance, 8(bits10-15) is z up
// gi.linkentity sets this properly
int sound; // for looping sounds, to guarantee shutoff
int event; // impulse events -- muzzle flashes, footsteps, etc
// events only go out for a single frame, they
// are automatically cleared each frame
} entity_state_t;
struct edict_s
{
entity_state_t s;
struct gclient_s *client;
qboolean inuse;
int linkcount;
// FIXME: move these fields to a server private sv_entity_t
link_t area; // linked to a division node or leaf
int num_clusters; // if -1, use headnode instead
int clusternums[MAX_ENT_CLUSTERS];
int headnode; // unused if num_clusters != -1
int areanum, areanum2;
//================================
int svflags; // SVF_NOCLIENT, SVF_DEADMONSTER, SVF_MONSTER, etc
vec3_t mins, maxs;
vec3_t absmin, absmax, size;
solid_t solid;
int clipmask;
edict_t *owner;
// the game dll can add anything it wants after
// this point in the structure
};
struct edict_s
{
entity_state_t s;
struct gclient_s *client; // NULL if not a player
// the server expects the first part
// of gclient_s to be a player_state_t
// but the rest of it is opaque
qboolean inuse;
int linkcount;
// FIXME: move these fields to a server private sv_entity_t
link_t area; // linked to a division node or leaf
int num_clusters; // if -1, use headnode instead
int clusternums[MAX_ENT_CLUSTERS];
int headnode; // unused if num_clusters != -1
int areanum, areanum2;
//================================
int svflags;
vec3_t mins, maxs;
vec3_t absmin, absmax, size;
solid_t solid;
int clipmask;
edict_t *owner;
// DO NOT MODIFY ANYTHING ABOVE THIS, THE SERVER
// EXPECTS THE FIELDS IN THAT ORDER!
//================================
int movetype;
int flags;
char *model;
float freetime; // sv.time when the object was freed
//
// only used locally in game, not by server
//
char *message;
char *classname;
int spawnflags;
float timestamp;
float angle; // set in qe3, -1 = up, -2 = down
char *target;
char *targetname;
char *killtarget;
char *team;
char *pathtarget;
char *deathtarget;
char *combattarget;
edict_t *target_ent;
float speed, accel, decel;
vec3_t movedir;
vec3_t pos1, pos2;
vec3_t velocity;
vec3_t avelocity;
int mass;
float air_finished;
float gravity; // per entity gravity multiplier (1.0 is normal)
// use for lowgrav artifact, flares
edict_t *goalentity;
edict_t *movetarget;
float yaw_speed;
float ideal_yaw;
float nextthink;
void (*prethink) (edict_t *ent);
void (*think)(edict_t *self);
void (*blocked)(edict_t *self, edict_t *other); //move to moveinfo?
void (*touch)(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
void (*use)(edict_t *self, edict_t *other, edict_t *activator);
void (*pain)(edict_t *self, edict_t *other, float kick, int damage);
void (*die)(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
float touch_debounce_time; // are all these legit? do we need more/less of them?
float pain_debounce_time;
float damage_debounce_time;
float fly_sound_debounce_time; //move to clientinfo
float last_move_time;
int health;
int max_health;
int gib_health;
int deadflag;
qboolean show_hostile;
float powerarmor_time;
char *map; // target_changelevel
int viewheight; // height above origin where eyesight is determined
int takedamage;
int dmg;
int radius_dmg;
float dmg_radius;
int sounds; //make this a spawntemp var?
int count;
edict_t *chain;
edict_t *enemy;
edict_t *oldenemy;
edict_t *activator;
edict_t *groundentity;
int groundentity_linkcount;
edict_t *teamchain;
edict_t *teammaster;
edict_t *mynoise; // can go in client only
edict_t *mynoise2;
int noise_index;
int noise_index2;
float volume;
float attenuation;
// timing variables
float wait;
float delay; // before firing targets
float random;
float teleport_time;
int watertype;
int waterlevel;
vec3_t move_origin;
vec3_t move_angles;
// move this to clientinfo?
int light_level;
int style; // also used as areaportal number
gitem_t *item; // for bonus items
// common data blocks
moveinfo_t moveinfo;
monsterinfo_t monsterinfo;
};
// entityState_t is the information conveyed from the server
// in an update message about entities that the client will
// need to render in some way
// Different eTypes may use the information in different ways
// The messages are delta compressed, so it doesn't really matter if
// the structure size is fairly large
typedef struct entityState_s {
int number; // entity index
int eType; // entityType_t
int eFlags;
trajectory_t pos; // for calculating position
trajectory_t apos; // for calculating angles
int time;
int time2;
vec3_t origin;
vec3_t origin2;
vec3_t angles;
vec3_t angles2;
int otherEntityNum; // shotgun sources, etc
int otherEntityNum2;
int groundEntityNum; // -1 = in air
int constantLight; // r + (g<<8) + (b<<16) + (intensity<<24)
int loopSound; // constantly loop this sound
int modelindex;
int modelindex2;
int clientNum; // 0 to (MAX_CLIENTS - 1), for players and corpses
int frame;
int solid; // for client side prediction, trap_linkentity sets this properly
int event; // impulse events -- muzzle flashes, footsteps, etc
int eventParm;
// for players
int powerups; // bit flags
int weapon; // determines weapon and flash model, etc
int legsAnim; // mask off ANIM_TOGGLEBIT
int torsoAnim; // mask off ANIM_TOGGLEBIT
int generic1;
} entityState_t;
// centity_t have a direct corespondence with gentity_t in the game, but
// only the entityState_t is directly communicated to the cgame
typedef struct centity_s {
entityState_t currentState; // from cg.frame
entityState_t nextState; // from cg.nextFrame, if available
qboolean interpolate; // true if next is valid to interpolate to
qboolean currentValid; // true if cg.frame holds this entity
int muzzleFlashTime; // move to playerEntity?
int previousEvent;
int teleportFlag;
int trailTime; // so missile trails can handle dropped initial packets
int dustTrailTime;
int miscTime;
int snapShotTime; // last time this entity was found in a snapshot
playerEntity_t pe;
int errorTime; // decay the error from this time
vec3_t errorOrigin;
vec3_t errorAngles;
qboolean extrapolated; // false if origin / angles is an interpolation
vec3_t rawOrigin;
vec3_t rawAngles;
vec3_t beamEnd;
// exact interpolated position of entity on this frame
vec3_t lerpOrigin;
vec3_t lerpAngles;
} centity_t;
struct gentity_s {
entityState_t s; // communicated by server to clients
entityShared_t r; // shared by both the server system and game
// DO NOT MODIFY ANYTHING ABOVE THIS, THE SERVER
// EXPECTS THE FIELDS IN THAT ORDER!
//================================
struct gclient_s *client; // NULL if not a client
qboolean inuse;
char *classname; // set in QuakeEd
int spawnflags; // set in QuakeEd
qboolean neverFree; // if true, FreeEntity will only unlink
// bodyque uses this
int flags; // FL_* variables
char *model;
char *model2;
int freetime; // level.time when the object was freed
int eventTime; // events will be cleared EVENT_VALID_MSEC after set
qboolean freeAfterEvent;
qboolean unlinkAfterEvent;
qboolean physicsObject; // if true, it can be pushed by movers and fall off edges
// all game items are physicsObjects,
float physicsBounce; // 1.0 = continuous bounce, 0.0 = no bounce
int clipmask; // brushes with this content value will be collided against
// when moving. items and corpses do not collide against
// players, for instance
// movers
moverState_t moverState;
int soundPos1;
int sound1to2;
int sound2to1;
int soundPos2;
int soundLoop;
gentity_t *parent;
gentity_t *nextTrain;
gentity_t *prevTrain;
vec3_t pos1, pos2;
char *message;
int timestamp; // body queue sinking, etc
float angle; // set in editor, -1 = up, -2 = down
char *target;
char *targetname;
char *team;
char *targetShaderName;
char *targetShaderNewName;
gentity_t *target_ent;
float speed;
vec3_t movedir;
int nextthink;
void (*think)(gentity_t *self);
void (*reached)(gentity_t *self); // movers call this when hitting endpoint
void (*blocked)(gentity_t *self, gentity_t *other);
void (*touch)(gentity_t *self, gentity_t *other, trace_t *trace);
void (*use)(gentity_t *self, gentity_t *other, gentity_t *activator);
void (*pain)(gentity_t *self, gentity_t *attacker, int damage);
void (*die)(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod);
int pain_debounce_time;
int fly_sound_debounce_time; // wind tunnel
int last_move_time;
int health;
qboolean takedamage;
int damage;
int splashDamage; // quad will increase this without increasing radius
int splashRadius;
int methodOfDeath;
int splashMethodOfDeath;
int count;
gentity_t *chain;
gentity_t *enemy;
gentity_t *activator;
gentity_t *teamchain; // next entity in team
gentity_t *teammaster; // master of the team
#ifdef MISSIONPACK
int kamikazeTime;
int kamikazeShockTime;
#endif
int watertype;
int waterlevel;
int noise_index;
// timing variables
float wait;
float random;
gitem_t *item; // for bonus items
};
typedef struct {
entityState_t s; // communicated by server to clients
qboolean linked; // qfalse if not in any good cluster
int linkcount;
int svFlags; // SVF_NOCLIENT, SVF_BROADCAST, etc
// only send to this client when SVF_SINGLECLIENT is set
// if SVF_CLIENTMASK is set, use bitmask for clients to send to (maxclients must be <= 32, up to the mod to enforce this)
int singleClient;
qboolean bmodel; // if false, assume an explicit mins / maxs bounding box
// only set by trap_SetBrushModel
vec3_t mins, maxs;
int contents; // CONTENTS_TRIGGER, CONTENTS_SOLID, CONTENTS_BODY, etc
// a non-solid entity should set to 0
vec3_t absmin, absmax; // derived from mins/maxs and origin + rotation
// currentOrigin will be used for all collision detection and world linking.
// it will not necessarily be the same as the trajectory evaluation for the current
// time, because each entity must be moved one at a time after time is advanced
// to avoid simultanious collision issues
vec3_t currentOrigin;
vec3_t currentAngles;
// when a trace call is made and passEntityNum != ENTITYNUM_NONE,
// an ent will be excluded from testing if:
// ent->s.number == passEntityNum (don't interact with self)
// ent->s.ownerNum = passEntityNum (don't interact with your own missiles)
// entity[ent->s.ownerNum].ownerNum = passEntityNum (don't interact with other missiles from owner)
int ownerNum;
} entityShared_t;
This code is from Xash3D (opensource version of GoldSrc)
typedef struct entvars_s
{
string_t classname;
string_t globalname;
vec3_t origin;
vec3_t oldorigin;
vec3_t velocity;
vec3_t basevelocity;
vec3_t clbasevelocity; // Base velocity that was passed in to server physics so
// client can predict conveyors correctly. Server zeroes it, so we need to store here, too.
vec3_t movedir;
vec3_t angles; // Model angles
vec3_t avelocity; // angle velocity (degrees per second)
vec3_t punchangle; // auto-decaying view angle adjustment
vec3_t v_angle; // Viewing angle (player only)
// For parametric entities
vec3_t endpos;
vec3_t startpos;
float impacttime;
float starttime;
int fixangle; // 0:nothing, 1:force view angles, 2:add avelocity
float idealpitch;
float pitch_speed;
float ideal_yaw;
float yaw_speed;
int modelindex;
string_t model;
int viewmodel; // player's viewmodel
int weaponmodel; // what other players see
vec3_t absmin; // BB max translated to world coord
vec3_t absmax; // BB max translated to world coord
vec3_t mins; // local BB min
vec3_t maxs; // local BB max
vec3_t size; // maxs - mins
float ltime;
float nextthink;
int movetype;
int solid;
int skin;
int body; // sub-model selection for studiomodels
int effects;
float gravity; // % of "normal" gravity
float friction; // inverse elasticity of MOVETYPE_BOUNCE
int light_level;
int sequence; // animation sequence
int gaitsequence; // movement animation sequence for player (0 for none)
float frame; // % playback position in animation sequences (0..255)
float animtime; // world time when frame was set
float framerate; // animation playback rate (-8x to 8x)
byte controller[4]; // bone controller setting (0..255)
byte blending[2]; // blending amount between sub-sequences (0..255)
float scale; // sprites and models rendering scale (0..255)
int rendermode;
float renderamt;
vec3_t rendercolor;
int renderfx;
float health;
float frags;
int weapons; // bit mask for available weapons
float takedamage;
int deadflag;
vec3_t view_ofs; // eye position
int button;
int impulse;
edict_t *chain; // Entity pointer when linked into a linked list
edict_t *dmg_inflictor;
edict_t *enemy;
edict_t *aiment; // entity pointer when MOVETYPE_FOLLOW
edict_t *owner;
edict_t *groundentity;
int spawnflags;
int flags;
int colormap; // lowbyte topcolor, highbyte bottomcolor
int team;
float max_health;
float teleport_time;
float armortype;
float armorvalue;
int waterlevel;
int watertype;
string_t target;
string_t targetname;
string_t netname;
string_t message;
float dmg_take;
float dmg_save;
float dmg;
float dmgtime;
string_t noise;
string_t noise1;
string_t noise2;
string_t noise3;
float speed;
float air_finished;
float pain_finished;
float radsuit_finished;
edict_t *pContainingEntity;
int playerclass;
float maxspeed;
float fov;
int weaponanim;
int pushmsec;
int bInDuck;
int flTimeStepSound;
int flSwimTime;
int flDuckTime;
int iStepLeft;
float flFallVelocity;
int gamestate;
int oldbuttons;
int groupinfo;
// For mods
int iuser1;
int iuser2;
int iuser3;
int iuser4;
float fuser1;
float fuser2;
float fuser3;
float fuser4;
vec3_t vuser1;
vec3_t vuser2;
vec3_t vuser3;
vec3_t vuser4;
edict_t *euser1;
edict_t *euser2;
edict_t *euser3;
edict_t *euser4;
} entvars_t;
// physent_t
typedef struct physent_s
{
char name[32]; // Name of model, or "player" or "world".
int player;
vec3_t origin; // Model's origin in world coordinates.
struct model_s *model; // only for bsp models
struct model_s *studiomodel; // SOLID_BBOX, but studio clip intersections.
vec3_t mins, maxs; // only for non-bsp models
int info; // For client or server to use to identify (index into edicts or cl_entities)
vec3_t angles; // rotated entities need this info for hull testing to work.
int solid; // Triggers and func_door type WATER brushes are SOLID_NOT
int skin; // BSP Contents for such things like fun_door water brushes.
int rendermode; // So we can ignore glass
// Complex collision detection.
float frame;
int sequence;
byte controller[4];
byte blending[2];
int movetype;
int takedamage;
int blooddecal;
int team;
int classnumber;
// For mods
int iuser1;
int iuser2;
int iuser3;
int iuser4;
float fuser1; // also contains pev->scale when "sv_allow_studio_scaling" is "1"
float fuser2;
float fuser3;
float fuser4;
vec3_t vuser1;
vec3_t vuser2;
vec3_t vuser3;
vec3_t vuser4;
} physent_t;
struct edict_s
{
qboolean free;
int serialnumber;
link_t area; // linked to a division node or leaf
int headnode; // -1 to use normal leaf check
int num_leafs;
#ifdef SUPPORT_BSP2_FORMAT
int leafnums[MAX_ENT_LEAFS];
#else
short leafnums[MAX_ENT_LEAFS];
#endif
float freetime; // sv.time when the object was freed
void* pvPrivateData; // Alloced and freed by engine, used by DLLs
entvars_t v; // C exported fields from progs
// other fields from progs come immediately after
};
typedef struct
{
char model[MAX_QPATH]; // curstate.modelindex = SV_ModelIndex
vec3_t tentOffset; // if attached, client origin + tentOffset = tent origin.
short clientIndex;
float fadeSpeed;
float bounceFactor;
byte hitSound;
qboolean high_priority;
float x, y, z;
int flags;
float time;
// base state
vec3_t velocity; // baseline.origin
vec3_t avelocity; // baseline.angles
int fadeamount; // baseline.renderamt
float sparklife; // baseline.framerate
float thinkTime; // baseline.scale
// current state
vec3_t origin; // entity.origin
vec3_t angles; // entity.angles
float renderamt; // curstate.renderamt
color24 rendercolor; // curstate.rendercolor
int rendermode; // curstate.rendermode
int renderfx; // curstate.renderfx
float framerate; // curstate.framerate
float frame; // curstate.frame
byte body; // curstate.body
byte skin; // curstate.skin
float scale; // curstate.scale
} tentlist_t;
struct cl_entity_s
{
int index; // Index into cl_entities ( should match actual slot, but not necessarily )
qboolean player; // True if this entity is a "player"
entity_state_t baseline; // The original state from which to delta during an uncompressed message
entity_state_t prevstate; // The state information from the penultimate message received from the server
entity_state_t curstate; // The state information from the last message received from server
int current_position; // Last received history update index
position_history_t ph[HISTORY_MAX]; // History of position and angle updates for this player
mouth_t mouth; // For synchronizing mouth movements.
latchedvars_t latched; // Variables used by studio model rendering routines
// Information based on interplocation, extrapolation, prediction, or just copied from last msg received.
//
float lastmove;
// Actual render position and angles
vec3_t origin;
vec3_t angles;
// Attachment points
vec3_t attachment[4];
// Other entity local information
int trivial_accept;
struct model_s *model; // cl.model_precache[ curstate.modelindes ]; all visible entities have a model
struct efrag_s *efrag; // linked list of efrags
struct mnode_s *topnode; // for bmodels, first world node that splits bmodel, or NULL if not split
float syncbase; // for client-side animations -- used by obsolete alias animation system, remove?
int visframe; // last frame this entity was found in an active leaf
colorVec cvFloorColor;
};
struct entity_state_s
{
// Fields which are filled in by routines outside of delta compression
int entityType;
// Index into cl_entities array for this entity.
int number;
float msg_time;
// Message number last time the player/entity state was updated.
int messagenum;
// Fields which can be transitted and reconstructed over the network stream
vec3_t origin;
vec3_t angles;
int modelindex;
int sequence;
float frame;
int colormap;
short skin;
short solid;
int effects;
float scale;
byte eflags;
// Render information
int rendermode;
int renderamt;
color24 rendercolor;
int renderfx;
int movetype;
float animtime;
float framerate;
int body;
byte controller[4];
byte blending[4];
vec3_t velocity;
// Send bbox down to client for use during prediction.
vec3_t mins;
vec3_t maxs;
int aiment;
// If owned by a player, the index of that player ( for projectiles ).
int owner;
// Friction, for prediction.
float friction;
// Gravity multiplier
float gravity;
// PLAYER SPECIFIC
int team;
int playerclass;
int health;
qboolean spectator;
int weaponmodel;
int gaitsequence;
// If standing on conveyor, e.g.
vec3_t basevelocity;
// Use the crouched hull, or the regular player hull.
int usehull;
// Latched buttons last time state updated.
int oldbuttons;
// -1 = in air, else pmove entity number
int onground;
int iStepLeft;
// How fast we are falling
float flFallVelocity;
float fov;
int weaponanim;
// Parametric movement overrides
vec3_t startpos;
vec3_t endpos;
float impacttime;
float starttime;
// For mods
int iuser1;
int iuser2;
int iuser3;
int iuser4;
float fuser1;
float fuser2;
float fuser3;
float fuser4;
vec3_t vuser1;
vec3_t vuser2;
vec3_t vuser3;
vec3_t vuser4;
};
This code is from The Operative: No One Lives Forever using the engine known as LithTech2.
class BaseClass
{
public:
BaseClass(uint8 nType=OT_NORMAL)
{
m_pFirstAggregate = NULL;
m_hObject = NULL;
m_nType = nType;
}
virtual ~BaseClass() {}
uint8 GetType() const { return m_nType; }
void SetType( uint8 type ) { m_nType = type; }
static ILTServer* GetServerDE() { return (ILTServer*)g_pLTServer; }
// If you derive from BaseClass, pass your messages down to here at the
// end of your message loop.
virtual uint32 EngineMessageFn(uint32 messageID, void *pData, LTFLOAT lData);
// Call this when you get an object message function so aggregates will get it.
virtual uint32 ObjectMessageFn(HOBJECT hSender, uint32 messageID, HMESSAGEREAD hRead);
protected:
void AddAggregate(LPAGGREGATE pAggregate);
LTBOOL RemoveAggregate(LPAGGREGATE pAggregate);
public: // Data members
// VERY Important that these data memebers stay in this order.
// This version and the C version must be the same!
LPAGGREGATE m_pFirstAggregate;// The first aggregate in the linked list..
// This is always set.. you can use this to pass in an
// HOBJECT to the functions that require on instead of calling
// ObjectToHandle() every time..
HOBJECT m_hObject;
// C++ only data...
uint8 m_nType; // Type of object (see ltbasedefs.h)
};
class GameBase : public BaseClass
{
public :
GameBase(uint8 nType=OT_NORMAL);
virtual ~GameBase();
virtual void CreateBoundingBox();
virtual void RemoveBoundingBox();
virtual void UpdateBoundingBox();
uint32 ObjectMessageFn(HOBJECT hSender, uint32 messageID, HMESSAGEREAD hRead);
ObjectList* GetMarkList() const { return m_pMarkList; }
void AddMark(HOBJECT hMark);
protected :
virtual uint32 EngineMessageFn(uint32 messageID, void *pData, LTFLOAT fData);
virtual LTVector GetBoundingBoxColor();
HOBJECT m_hDimsBox;
ObjectList* m_pMarkList;
uint32 m_dwOriginalFlags;
uint32 m_nSaveVersion;
HSTRING m_hstrSave;
enum eUpdateControl
{
eControlDeactivation=0,
eControlUpdateOnly
};
virtual void SetNextUpdate(LTFLOAT fDelta, eUpdateControl eControl=eControlDeactivation);
private :
void TriggerMsg(HOBJECT hSender, const char* pMsg);
void HandleLinkBroken(HOBJECT hObj);
void Save(HMESSAGEWRITE hWrite);
void Load(HMESSAGEREAD hRead);
};
This code is from a disassembly/reverse-engineering of The Legend of Zelda - Ocarina of Time
typedef struct Actor {
/* 0x000 */ s16 id; // Actor ID
/* 0x002 */ u8 category; // Actor category. Refer to the corresponding enum for values
/* 0x003 */ s8 room; // Room number the actor is in. -1 denotes that the actor won't despawn on a room change
/* 0x004 */ u32 flags; // Flags used for various purposes
/* 0x008 */ PosRot home; // Initial position/rotation when spawned. Can be used for other purposes
/* 0x01C */ s16 params; // Configurable variable set by the actor's spawn data; original name: "args_data"
/* 0x01E */ s8 objBankIndex; // Object bank index of the actor's object dependency; original name: "bank"
/* 0x01F */ s8 targetMode; // Controls how far the actor can be targeted from and how far it can stay locked on
/* 0x020 */ u16 sfx; // SFX ID to play. Sound plays when value is set, then is cleared the following update cycle
/* 0x024 */ PosRot world; // Position/rotation in the world
/* 0x038 */ PosRot focus; // Target reticle focuses on this position. For player this represents head pos and rot
/* 0x04C */ f32 targetArrowOffset; // Height offset of the target arrow relative to `focus` position
/* 0x050 */ Vec3f scale; // Scale of the actor in each axis
/* 0x05C */ Vec3f velocity; // Velocity of the actor in each axis
/* 0x068 */ f32 speedXZ; // How fast the actor is traveling along the XZ plane
/* 0x06C */ f32 gravity; // Acceleration due to gravity. Value is added to Y velocity every frame
/* 0x070 */ f32 minVelocityY; // Sets the lower bounds cap on velocity along the Y axis
/* 0x074 */ CollisionPoly* wallPoly; // Wall polygon the actor is touching
/* 0x078 */ CollisionPoly* floorPoly; // Floor polygon directly below the actor
/* 0x07C */ u8 wallBgId; // Bg ID of the wall polygon the actor is touching
/* 0x07D */ u8 floorBgId; // Bg ID of the floor polygon directly below the actor
/* 0x07E */ s16 wallYaw; // Y rotation of the wall polygon the actor is touching
/* 0x080 */ f32 floorHeight; // Y position of the floor polygon directly below the actor
/* 0x084 */ f32 yDistToWater; // Distance to the surface of active waterbox. Negative value means above water
/* 0x088 */ u16 bgCheckFlags; // See comments below actor struct for wip docs. TODO: macros for these flags
/* 0x08A */ s16 yawTowardsPlayer; // Y rotation difference between the actor and the player
/* 0x08C */ f32 xyzDistToPlayerSq; // Squared distance between the actor and the player in the x,y,z axis
/* 0x090 */ f32 xzDistToPlayer; // Distance between the actor and the player in the XZ plane
/* 0x094 */ f32 yDistToPlayer; // Dist is negative if the actor is above the player
/* 0x098 */ CollisionCheckInfo colChkInfo; // Variables related to the Collision Check system
/* 0x0B4 */ ActorShape shape; // Variables related to the physical shape of the actor
/* 0x0E4 */ Vec3f projectedPos; // Position of the actor in projected space
/* 0x0F0 */ f32 projectedW; // w component of the projected actor position
/* 0x0F4 */ f32 uncullZoneForward; // Amount to increase the uncull zone forward by (in projected space)
/* 0x0F8 */ f32 uncullZoneScale; // Amount to increase the uncull zone scale by (in projected space)
/* 0x0FC */ f32 uncullZoneDownward; // Amount to increase uncull zone downward by (in projected space)
/* 0x100 */ Vec3f prevPos; // World position from the previous update cycle
/* 0x10C */ u8 isTargeted; // Set to true if the actor is currently being targeted by the player
/* 0x10D */ u8 targetPriority; // Lower values have higher priority. Resets to 0 when player stops targeting
/* 0x10E */ u16 textId; // Text ID to pass to link/display when interacting with the actor
/* 0x110 */ u16 freezeTimer; // Actor does not update when set. Timer decrements automatically
/* 0x112 */ u16 colorFilterParams; // Set color filter to red, blue, or white. Toggle opa or xlu
/* 0x114 */ u8 colorFilterTimer; // A non-zero value enables the color filter. Decrements automatically
/* 0x115 */ u8 isDrawn; // Set to true if the actor is currently being drawn. Always stays false for lens actors
/* 0x116 */ u8 dropFlag; // Configures what item is dropped by the actor from `Item_DropCollectibleRandom`
/* 0x117 */ u8 naviEnemyId; // Sets what 0600 dialog to display when talking to navi. Default 0xFF
/* 0x118 */ struct Actor* parent; // Usage is actor specific. Set if actor is spawned via `Actor_SpawnAsChild`
/* 0x11C */ struct Actor* child; // Usage is actor specific. Set if actor is spawned via `Actor_SpawnAsChild`
/* 0x120 */ struct Actor* prev; // Previous actor of this category
/* 0x124 */ struct Actor* next; // Next actor of this category
/* 0x128 */ ActorFunc init; // Initialization Routine. Called by `Actor_Init` or `Actor_UpdateAll`
/* 0x12C */ ActorFunc destroy; // Destruction Routine. Called by `Actor_Destroy`
/* 0x130 */ ActorFunc update; // Update Routine. Called by `Actor_UpdateAll`
/* 0x134 */ ActorFunc draw; // Draw Routine. Called by `Actor_Draw`
/* 0x138 */ ActorOverlay* overlayEntry; // Pointer to the overlay table entry for this actor
/* 0x13C */ char dbgPad[0x10]; // Padding that only exists in the debug rom
} Actor; // size = 0x14C
This code is from a disassembly/reverse-engineering of Super Mario 64, that has even spawned a port.
struct Object
{
/*0x000*/ struct ObjectNode header;
/*0x068*/ struct Object *parentObj;
/*0x06C*/ struct Object *prevObj;
/*0x070*/ u32 collidedObjInteractTypes;
/*0x074*/ s16 activeFlags;
/*0x076*/ s16 numCollidedObjs;
/*0x078*/ struct Object *collidedObjs[4];
/*0x088*/
union
{
// Object fields. See object_fields.h.
u32 asU32[0x50];
s32 asS32[0x50];
s16 asS16[0x50][2];
f32 asF32[0x50];
#if !IS_64_BIT
s16 *asS16P[0x50];
s32 *asS32P[0x50];
struct Animation **asAnims[0x50];
struct Waypoint *asWaypoint[0x50];
struct ChainSegment *asChainSegment[0x50];
struct Object *asObject[0x50];
struct Surface *asSurface[0x50];
void *asVoidPtr[0x50];
const void *asConstVoidPtr[0x50];
#endif
} rawData;
#if IS_64_BIT
union {
s16 *asS16P[0x50];
s32 *asS32P[0x50];
struct Animation **asAnims[0x50];
struct Waypoint *asWaypoint[0x50];
struct ChainSegment *asChainSegment[0x50];
struct Object *asObject[0x50];
struct Surface *asSurface[0x50];
void *asVoidPtr[0x50];
const void *asConstVoidPtr[0x50];
} ptrData;
#endif
/*0x1C8*/ u32 unused1;
/*0x1CC*/ const BehaviorScript *curBhvCommand;
/*0x1D0*/ u32 bhvStackIndex;
/*0x1D4*/ uintptr_t bhvStack[8];
/*0x1F4*/ s16 bhvDelayTimer;
/*0x1F6*/ s16 respawnInfoType;
/*0x1F8*/ f32 hitboxRadius;
/*0x1FC*/ f32 hitboxHeight;
/*0x200*/ f32 hurtboxRadius;
/*0x204*/ f32 hurtboxHeight;
/*0x208*/ f32 hitboxDownOffset;
/*0x20C*/ const BehaviorScript *behavior;
/*0x210*/ u32 unused2;
/*0x214*/ struct Object *platform;
/*0x218*/ void *collisionData;
/*0x21C*/ Mat4 transform;
/*0x25C*/ void *respawnInfo;
};
This code is the engine from Cube 2: Sauerbraten, also used in Assault Cube and other opensource FPS games.
struct entity // persistent map entity
{
vec o; // position
short attr1, attr2, attr3, attr4, attr5;
uchar type; // type is one of the above
uchar reserved;
};
struct extentity : entity // part of the entity that doesn't get saved to disk
{
int flags; // the only dynamic state of a map entity
entitylight light;
extentity *attached;
extentity() : flags(0), attached(NULL) {}
bool spawned() const { return (flags&EF_SPAWNED) != 0; }
void setspawned(bool val) { if(val) flags |= EF_SPAWNED; else flags &= ~EF_SPAWNED; }
void setspawned() { flags |= EF_SPAWNED; }
void clearspawned() { flags &= ~EF_SPAWNED; }
};
struct physent // base entity type, can be affected by physics
{
vec o, vel, falling; // origin, velocity
vec deltapos, newpos; // movement interpolation
float yaw, pitch, roll;
float maxspeed; // cubes per second, 100 for player
int timeinair;
float radius, eyeheight, aboveeye; // bounding box size
float xradius, yradius, zmargin;
vec floor; // the normal of floor the dynent is on
int inwater;
bool jumping;
char move, strafe;
uchar physstate; // one of PHYS_* above
uchar state, editstate; // one of CS_* above
uchar type; // one of ENT_* above
uchar collidetype; // one of COLLIDE_* above
bool blocked; // used by physics to signal ai
physent() : o(0, 0, 0), deltapos(0, 0, 0), newpos(0, 0, 0), yaw(0), pitch(0), roll(0), maxspeed(100),
radius(4.1f), eyeheight(14), aboveeye(1), xradius(4.1f), yradius(4.1f), zmargin(0),
state(CS_ALIVE), editstate(CS_ALIVE), type(ENT_PLAYER),
collidetype(COLLIDE_ELLIPSE),
blocked(false)
{ reset(); }
void resetinterp()
{
newpos = o;
deltapos = vec(0, 0, 0);
}
void reset()
{
inwater = 0;
timeinair = 0;
jumping = false;
strafe = move = 0;
physstate = PHYS_FALL;
vel = falling = vec(0, 0, 0);
floor = vec(0, 0, 1);
}
vec feetpos(float offset = 0) const { return vec(o).add(vec(0, 0, offset - eyeheight)); }
vec headpos(float offset = 0) const { return vec(o).add(vec(0, 0, offset)); }
bool maymove() const { return timeinair || physstate < PHYS_FLOOR || vel.squaredlen() > 1e-4f || deltapos.squaredlen() > 1e-4f; }
};
struct dynent : physent // animated characters, or characters that can receive input
{
bool k_left, k_right, k_up, k_down; // see input code
entitylight light;
animinterpinfo animinterp[MAXANIMPARTS];
ragdolldata *ragdoll;
occludequery *query;
int occluded, lastrendered;
dynent() : ragdoll(NULL), query(NULL), occluded(0), lastrendered(0)
{
reset();
}
~dynent()
{
#ifndef STANDALONE
extern void cleanragdoll(dynent *d);
if(ragdoll) cleanragdoll(this);
#endif
}
void stopmoving()
{
k_left = k_right = k_up = k_down = jumping = false;
move = strafe = 0;
}
void reset()
{
physent::reset();
stopmoving();
loopi(MAXANIMPARTS) animinterp[i].reset();
}
vec abovehead() { return vec(o).add(vec(0, 0, aboveeye+4)); }
};
struct fpsent : dynent, fpsstate
{
int weight; // affects the effectiveness of hitpush
int clientnum, privilege, lastupdate, plag, ping;
int lifesequence; // sequence id for each respawn, used in damage test
int respawned, suicided;
int lastpain;
int lastaction, lastattackgun;
bool attacking;
int attacksound, attackchan, idlesound, idlechan;
int lasttaunt;
int lastpickup, lastpickupmillis, lastbase, lastrepammo, flagpickup, tokens;
vec lastcollect;
int frags, flags, deaths, totaldamage, totalshots;
editinfo *edit;
float deltayaw, deltapitch, deltaroll, newyaw, newpitch, newroll;
int smoothmillis;
string name, team, info;
int playermodel;
ai::aiinfo *ai;
int ownernum, lastnode;
vec muzzle;
fpsent() : weight(100), clientnum(-1), privilege(PRIV_NONE), lastupdate(0), plag(0), ping(0), lifesequence(0), respawned(-1), suicided(-1), lastpain(0), attacksound(-1), attackchan(-1), idlesound(-1), idlechan(-1), frags(0), flags(0), deaths(0), totaldamage(0), totalshots(0), edit(NULL), smoothmillis(-1), playermodel(-1), ai(NULL), ownernum(-1), muzzle(-1, -1, -1)
{
name[0] = team[0] = info[0] = 0;
respawn();
}
~fpsent()
{
freeeditinfo(edit);
if(attackchan >= 0) stopsound(attacksound, attackchan);
if(idlechan >= 0) stopsound(idlesound, idlechan);
if(ai) delete ai;
}
void hitpush(int damage, const vec &dir, fpsent *actor, int gun)
{
vec push(dir);
push.mul((actor==this && guns[gun].exprad ? EXP_SELFPUSH : 1.0f)*guns[gun].hitpush*damage/weight);
vel.add(push);
}
void stopattacksound()
{
if(attackchan >= 0) stopsound(attacksound, attackchan, 250);
attacksound = attackchan = -1;
}
void stopidlesound()
{
if(idlechan >= 0) stopsound(idlesound, idlechan, 100);
idlesound = idlechan = -1;
}
void respawn()
{
dynent::reset();
fpsstate::respawn();
respawned = suicided = -1;
lastaction = 0;
lastattackgun = gunselect;
attacking = false;
lasttaunt = 0;
lastpickup = -1;
lastpickupmillis = 0;
lastbase = lastrepammo = -1;
flagpickup = 0;
tokens = 0;
lastcollect = vec(-1e10f, -1e10f, -1e10f);
stopattacksound();
lastnode = -1;
}
};
洞窟物語 (Doukutsu Monogatari) is also known as Cave Story. This code is from the NXEngine - an opensource rewrite of the original engine by Pixel.
class Object
{
public:
virtual ~Object() { } // REQUIRED for subclasses (e.g. Player)
void SetType(int type);
void ChangeType(int type);
void BringToFront();
void PushBehind(Object *behind);
void PushBehind(int objtype);
// --------------------------------------- hit detection w/ map
uint32_t GetAttributes(const Point *pointlist, int npoints, int *tile = NULL);
bool CheckAttribute(const Point *pointlist, int npoints, uint32_t attrmask, \
int *tile_x = NULL, int *tile_y = NULL);
bool CheckSolidIntersect(Object *other, const Point *pointlist, int npoints);
// --------------------------------------- overridden convenience versions of above
bool CheckAttribute(SIFPointList *points, uint32_t attrmask, int *tile_x = NULL, int *tile_y = NULL)
{
return CheckAttribute(&points->point[0], points->count, attrmask, tile_x, tile_y);
}
uint32_t GetAttributes(SIFPointList *points, int *tile = NULL)
{
return GetAttributes(&points->point[0], points->count, tile);
}
bool CheckSolidIntersect(Object *other, SIFPointList *points)
{
return CheckSolidIntersect(other, &points->point[0], points->count);
}
// ---------------------------------------
void UpdateBlockStates(uint8_t updatemask);
void SetBlockForSolidBrick(uint8_t updatemask);
int GetBlockingType();
// ---------------------------------------
bool apply_xinertia(int inertia);
bool apply_yinertia(int inertia);
void PushPlayerOutOfWay(int xinertia, int yinertia);
void SnapToGround();
// ---------------------------------------
void DealDamage(int dmg, Object *shot = NULL);
void Kill();
void SpawnPowerups();
void SpawnXP(int amt);
// ---------------------------------------
void RunAI();
void DealContactDamage();
int GetAttackDirection();
void OnTick();
void OnAftermove();
void OnSpawn();
void OnDeath();
// ---------------------------------------
void animate_seq(int speed, const int *framelist, int nframes);
void CurlyTargetHere(int mintime = 80, int maxtime = 100);
void ResetClip();
void MoveAtDir(int dir, int speed);
// ---------------------------------------
void Delete(); // mark for deletion at end of frame
void Destroy(); // delete immediately
void DisconnectGamePointers();
// ---------------------------------------
int Width();
int Height();
int BBoxWidth();
int BBoxHeight();
int CenterX();
int CenterY();
int Left();
int Right();
int Top();
int Bottom();
int SolidLeft();
int SolidRight();
int SolidTop();
int SolidBottom();
int ActionPointX();
int ActionPointY();
int ActionPoint2X();
int ActionPoint2Y();
int DrawPointX();
int DrawPointY();
SIFSprite *Sprite();
// ---------------------------------------
int type; // object's type
int sprite; // sprite # to use with object
int frame; // frame of sprite to display
int x, y;
int xinertia, yinertia;
uint8_t dir;
int hp; // remaining health
int damage; // if != 0 does this much damage to player on touch
int state; // AI state
int substate; // state of current "common/shared" AI routine
int dirparam; // for ANP's that use the dir as an extra parameter: see tsc.cpp: SetCSDir
// if the object has FLAG_SOLID_BRICK set, this is how much damage it does to the
// player if it runs him into a wall or the ceiling/floor.
int smushdamage;
// for enemies' "shaking" effect when hurt
int shaketime;
int display_xoff;
// rising damage points
FloatText *DamageText;
// tracks amount of damage dealt quickly, while the objects is still shaking from
// previous shots. displaying this damage is postponed until the enemy stops shaking.
int DamageWaiting;
// for teleportation and other effects
bool clip_enable;
int clipx1, clipx2;
int clipy1, clipy2;
// for use by AI
int timer, timer2, timer3;
int animtimer;
int animframe;
int blinktimer;
int xmark, ymark;
int xmark2, ymark2;
uint8_t angle, angleoffset; // used for a few such as skullstep
int speed;
int savedhp;
uint32_t flags; // NPC flags (from .pxe)
uint32_t nxflags; // NXEngine-specific flags
uint16_t id1, id2; // object identifiers (from .pxe)
bool onscreen; // true if currently onscreen (lags 1 frame behind)
bool invisible; // if true the object will NOT be rendered (but still does collision checking)
// flags which are set if an object is touching a wall, ceiling, or floor
// they're addressable either by the array or individually.
#define BLOCKED_MAP 1
#define BLOCKED_OBJECT 2
union {
struct { uint8_t blockr, blockl, blocku, blockd; };
uint8_t block[4];
};
union {
struct { uint8_t lastblockr, lastblockl, lastblocku, lastblockd; };
uint8_t lastblock[4];
};
// if true, object has been deleted and should be freed before next tick
bool deleted;
// the dual-layered linked-list. one list is order of creation is the
// order AI routines are run in, the other is the z-order and is the
// order the objects are drawn in.
Object *prev, *next;
Object *lower, *higher;
Object *linkedobject;
// AI variables used for specific AI functions
union
{
// for player shots (not enemy shots)
struct
{
int ttl; // frames left till shot times out; sets range
int dir; // direction shot was fired in, LEFT RIGHT UP DOWN.
int damage; // damage dealt per hit
int btype; // bullet type
int level; // weapon level (0, 1, or 2)
// missile boom spawner used w/ player missiles
struct
{
int range;
int booms_left;
} boomspawner;
} shot;
struct
{
int bultype;
int nlayers;
int wave_amt;
} mgun;
struct
{ // if 1 on an OBJ_CARRIED_OBJECT then object faces in OPPOSITE direction of carrier
bool flip;
} carry;
struct
{
int jumpheight, jumpgrav;
int falldmg;
bool canfly;
} critter;
struct
{
int blockedtime;
int reachptimer;
int tryjumptime;
int impjumptime;
uchar impjump;
uchar look;
int gunsprite;
int changedirtimer;
bool spawned_watershield;
} curly;
struct
{
bool left_ground;
} toro;
struct
{
Object *carried_by;
} sue;
struct
{
bool smoking;
int smoketimer;
} balrog;
struct
{
bool fireattack;
} igor;
struct
{
bool is_horizontal;
int x1, y1, x2, y2;
} hvt; // hvtrigger
struct
{
Object *layers[4];
} cloud;
};
};
The Xray engine is the engine used in STALKER: Shadow of Chernobyl and its sequels. This code is from OpenXRay - an enhanced, opensource port of the XRay engine.
class CEntity : public CPhysicsShellHolder, public CDamageManager
{
friend class CEntityCondition;
private:
typedef CPhysicsShellHolder inherited;
CEntityConditionSimple* m_entity_condition;
protected:
//время через которое мертвое тело убирется с уровня
ALife::_TIME_ID m_dwBodyRemoveTime;
protected:
virtual CEntityConditionSimple* create_entity_condition(CEntityConditionSimple* ec);
public:
IC float GetfHealth() const { return m_entity_condition->GetHealth(); }
IC float SetfHealth(float value)
{
m_entity_condition->SetHealth(value);
return value;
}
float m_fMorale;
// Team params
int id_Team;
int id_Squad;
int id_Group;
virtual void ChangeTeam(int team, int squad, int group);
struct SEntityState
{
u32 bJump : 1;
u32 bCrouch : 1;
u32 bFall : 1;
u32 bSprint : 1;
float fVelocity;
float fAVelocity;
};
float m_fFood;
// General
CEntity();
virtual ~CEntity();
virtual IFactoryObject* _construct();
virtual CEntity* cast_entity() { return this; }
public:
// Core events
virtual void Load(LPCSTR section);
virtual void reinit();
virtual void reload(LPCSTR section);
virtual bool net_Spawn(CSE_Abstract* DC);
virtual void net_Destroy();
virtual void shedule_Update(u32 dt);
bool IsFocused() const;
bool IsMyCamera() const;
// virtual float g_Health ()const { return GetfHealth();}
/* virtual*/ IC float GetMaxHealth() const { return m_entity_condition->max_health(); }
/* virtual*/ IC void SetMaxHealth(float v) { m_entity_condition->max_health() = v; }
/*virtual*/ IC BOOL g_Alive() const { return GetfHealth() > 0; }
virtual BOOL g_State(SEntityState&) const { return FALSE; }
bool AlreadyDie() { return 0 != GetLevelDeathTime() ? true : false; }
ALife::_TIME_ID GetGameDeathTime() const { return m_game_death_time; }
u32 GetLevelDeathTime() const { return m_level_death_time; }
virtual float CalcCondition(float hit);
// if false - hits go through and dont hit
virtual bool in_solid_state() { return true; }
int g_Team() const { return id_Team; }
int g_Squad() const { return id_Squad; }
int g_Group() const { return id_Group; }
// Health calculations
virtual void Hit(SHit* pHDS);
virtual void HitSignal(float P, Fvector& local_dir, IGameObject* who, s16 element) = 0;
virtual void HitImpulse(float P, Fvector& vWorldDir, Fvector& vLocalDir) = 0;
virtual void Die(IGameObject* who);
//void KillEntity (IGameObject* who);
void KillEntity(u16 whoID, bool bypass_actor_check = false);
// Events
virtual void OnEvent(NET_Packet& P, u16 type);
virtual BOOL IsVisibleForHUD() { return g_Alive(); }
virtual void g_fireParams(const CHudItem*, Fvector&, Fvector&){};
virtual bool g_stateFire() { return true; }
// time of entity death
u32 m_level_death_time;
ALife::_TIME_ID m_game_death_time;
void set_death_time();
virtual void set_ready_to_save();
private:
ALife::_OBJECT_ID m_killer_id;
public:
IC u16 killer_id() const { return m_killer_id; };
virtual bool use_simplified_visual() const { return false; };
public:
virtual void on_before_change_team();
virtual void on_after_change_team();
private:
bool m_registered_member;
};
class CEntityAlive : public CEntity
{
protected:
using inherited = CEntity;
private:
u32 m_used_time;
public:
virtual CEntityAlive* cast_entity_alive() { return this; }
bool m_bMobility;
float m_fAccuracy;
float m_fIntelligence;
u32 m_use_timeout;
u8 m_squad_index;
private:
bool m_is_agresive;
bool m_is_start_attack;
// m_PhysicMovementControl
// CPHMovementControl *m_PhysicMovementControl;
public:
// General
CEntityAlive();
virtual ~CEntityAlive();
// Core events
virtual IFactoryObject* _construct();
virtual void Load(LPCSTR section);
virtual void reinit();
virtual void reload(LPCSTR section);
// object serialization
virtual void save(NET_Packet& output_packet);
virtual void load(IReader& input_packet);
virtual bool net_Spawn(CSE_Abstract* DC);
virtual void net_Destroy();
virtual bool net_SaveRelevant();
virtual void shedule_Update(u32 dt);
virtual void create_anim_mov_ctrl(CBlend* b, Fmatrix* start_pose, bool local_animation);
virtual void destroy_anim_mov_ctrl();
virtual void HitImpulse(float amount, Fvector& vWorldDir, Fvector& vLocalDir);
virtual void Hit(SHit* pHDS);
virtual void Die(IGameObject* who);
virtual void g_WeaponBones(int& L, int& R1, int& R2) = 0;
void set_lock_corpse(bool b_l_corpse);
bool is_locked_corpse();
// virtual float GetfHealth () const;
// virtual float SetfHealth (float value);
// virtual float g_Health () const;
// virtual float g_MaxHealth () const;
virtual float g_Radiation() const;
virtual float SetfRadiation(float value);
virtual float CalcCondition(float hit);
// Visibility related
virtual float ffGetFov() const = 0;
virtual float ffGetRange() const = 0;
virtual bool human_being() const { return false; }
public:
// IC CPHMovementControl* PMovement () {return
// m_PhysicMovementControl;}
virtual u16 PHGetSyncItemsNumber();
virtual CPHSynchronize* PHGetSyncItem(u16 item);
virtual void PHUnFreeze();
virtual void PHFreeze();
virtual void PHGetLinearVell(Fvector& velocity);
virtual CPHSoundPlayer* ph_sound_player();
virtual CIKLimbsController* character_ik_controller();
virtual ICollisionHitCallback* get_collision_hit_callback();
virtual void set_collision_hit_callback(ICollisionHitCallback* cc);
protected:
using WOUND_VECTOR = xr_vector<CWound*>;
WOUND_VECTOR m_ParticleWounds;
virtual void StartFireParticles(CWound* pWound);
virtual void UpdateFireParticles();
virtual void LoadFireParticles(LPCSTR section);
public:
static void UnloadFireParticles();
protected:
static STR_VECTOR* m_pFireParticlesVector;
static u32 m_dwMinBurnTime;
static float m_fStartBurnWoundSize;
static float m_fStopBurnWoundSize;
virtual void BloodyWallmarks(float P, const Fvector& dir, s16 element, const Fvector& position_in_object_space);
static void LoadBloodyWallmarks(LPCSTR section);
public:
static void UnloadBloodyWallmarks();
void ClearBloodWounds() { m_BloodWounds.clear(); };
protected:
virtual void PlaceBloodWallmark(const Fvector& dir, const Fvector& start_pos, float trace_dist, float wallmark_size,
IWallMarkArray* pwallmarks_vector);
//информация о кровавых отметках на стенах, общая для всех CEntityAlive
static FactoryPtr<IWallMarkArray>* m_pBloodMarksVector;
static float m_fBloodMarkSizeMax;
static float m_fBloodMarkSizeMin;
static float m_fBloodMarkDistance;
static float m_fNominalHit;
//текстурки капель крови
static FactoryPtr<IWallMarkArray>* m_pBloodDropsVector;
//список ран с которых капает кровь
WOUND_VECTOR m_BloodWounds;
//размер раны, чтоб начала капать кровь
static float m_fStartBloodWoundSize;
//размер раны, чтоб остановить кровь
static float m_fStopBloodWoundSize;
static float m_fBloodDropSize;
//обновление ран, и рисование отметок от капающей крови
virtual void StartBloodDrops(CWound* pWound);
virtual void UpdateBloodDrops();
//отношения между существами и персонажами в зоне
public:
virtual ALife::ERelationType tfGetRelationType(const CEntityAlive* tpEntityAlive) const;
virtual bool is_relation_enemy(const CEntityAlive* tpEntityAlive) const;
MONSTER_COMMUNITY* monster_community;
private:
CEntityCondition* m_entity_condition;
CMaterialManager* m_material_manager;
bool b_eating;
protected:
virtual CEntityConditionSimple* create_entity_condition(CEntityConditionSimple* ec);
public:
IC CEntityCondition& conditions() const;
IC CMaterialManager& material() const
{
VERIFY(m_material_manager);
return (*m_material_manager);
}
protected:
u32 m_ef_creature_type;
u32 m_ef_weapon_type;
u32 m_ef_detector_type;
public:
virtual u32 ef_creature_type() const;
virtual u32 ef_weapon_type() const;
virtual u32 ef_detector_type() const;
virtual void OnHitHealthLoss(float NewHealth){}; //вызывается если entity теряет здоровье
virtual void OnCriticalHitHealthLoss(){}; //вызывается если entity умрет от хита
virtual void OnCriticalWoundHealthLoss(){}; //вызывается если entity умрет от потери крови
virtual void OnCriticalRadiationHealthLoss(){}; //вызывается если entity умрет от радиации
virtual CVisualMemoryManager* visual_memory() const { return (0); }
virtual void net_Relcase(IGameObject* O);
virtual Fvector predict_position(const float& time_to_check) const;
virtual Fvector target_position() const;
IC bool const& is_agresive() const;
IC void is_agresive(bool const& value);
IC bool const& is_start_attack() const;
IC void is_start_attack(bool const& value);
virtual Fvector get_new_local_point_on_mesh(u16& bone_id) const;
virtual Fvector get_last_local_point_on_mesh(Fvector const& last_point, u16 bone_id) const;
virtual void OnChangeVisual();
private:
void fill_hit_bone_surface_areas() const;
typedef xr_vector<std::pair<u16, float>> hit_bone_surface_areas_type;
mutable hit_bone_surface_areas_type m_hit_bone_surface_areas;
mutable CRandom m_hit_bones_random;
mutable bool m_hit_bone_surface_areas_actual;
};
class CActor : public CEntityAlive,
public IInputReceiver,
public Feel::Touch,
public CInventoryOwner,
public CPhraseDialogManager,
public CStepManager,
public Feel::Sound
#ifdef DEBUG
,
public pureRender
#endif
{
friend class CActorCondition;
private:
typedef CEntityAlive inherited;
public:
CActor();
virtual ~CActor();
public:
virtual bool AlwaysTheCrow() { return true; }
virtual CAttachmentOwner* cast_attachment_owner() { return this; }
virtual CInventoryOwner* cast_inventory_owner() { return this; }
virtual CActor* cast_actor() { return this; }
virtual CGameObject* cast_game_object() { return this; }
virtual IInputReceiver* cast_input_receiver() { return this; }
virtual CCharacterPhysicsSupport* character_physics_support() { return m_pPhysics_support; }
virtual CCharacterPhysicsSupport* character_physics_support() const { return m_pPhysics_support; }
virtual CPHDestroyable* ph_destroyable();
CHolderCustom* Holder() { return m_holder; }
public:
virtual void Load(LPCSTR section);
virtual void shedule_Update(u32 T);
virtual void UpdateCL();
virtual void OnEvent(NET_Packet& P, u16 type);
// Render
void renderable_Render(IRenderable* root) override;
virtual bool renderable_ShadowGenerate();
void feel_sound_new(IGameObject* who, int type, const CSound_UserDataPtr& user_data,
const Fvector& position, float power) override;
virtual Feel::Sound* dcast_FeelSound() { return this; }
float m_snd_noise;
#ifdef DEBUG
virtual void OnRender();
#endif
public:
virtual bool OnReceiveInfo(shared_str info_id) const;
virtual void OnDisableInfo(shared_str info_id) const;
virtual void NewPdaContact(CInventoryOwner*);
virtual void LostPdaContact(CInventoryOwner*);
#ifdef DEBUG
void DumpTasks();
#endif
protected:
virtual void AddEncyclopediaArticle(const CInfoPortion* info_portion) const;
struct SDefNewsMsg
{
GAME_NEWS_DATA* news_data;
u32 time;
bool operator<(const SDefNewsMsg& other) const { return time > other.time; }
};
xr_vector<SDefNewsMsg> m_defferedMessages;
void UpdateDefferedMessages();
public:
void AddGameNews_deffered(GAME_NEWS_DATA& news_data, u32 delay);
virtual void AddGameNews(GAME_NEWS_DATA& news_data);
void ClearGameNews();
protected:
CActorStatisticMgr* m_statistic_manager;
public:
virtual void StartTalk(CInventoryOwner* talk_partner);
void RunTalkDialog(CInventoryOwner* talk_partner, bool disable_break);
CActorStatisticMgr& StatisticMgr() { return *m_statistic_manager; }
CEncyclopediaRegistryWrapper* encyclopedia_registry;
CGameNewsRegistryWrapper* game_news_registry;
CCharacterPhysicsSupport* m_pPhysics_support;
virtual LPCSTR Name() const { return CInventoryOwner::Name(); }
public:
// PhraseDialogManager
virtual void ReceivePhrase(DIALOG_SHARED_PTR& phrase_dialog);
virtual void UpdateAvailableDialogs(CPhraseDialogManager* partner);
virtual void TryToTalk();
bool OnDialogSoundHandlerStart(CInventoryOwner* inv_owner, LPCSTR phrase);
bool OnDialogSoundHandlerStop(CInventoryOwner* inv_owner);
virtual void reinit();
virtual void reload(LPCSTR section);
virtual bool use_bolts() const;
virtual void OnItemTake(CInventoryItem* inventory_item);
virtual void OnItemRuck(CInventoryItem* inventory_item, const SInvItemPlace& previous_place);
virtual void OnItemBelt(CInventoryItem* inventory_item, const SInvItemPlace& previous_place);
virtual void OnItemDrop(CInventoryItem* inventory_item, bool just_before_destroy);
virtual void OnItemDropUpdate();
void MoveArtefactBelt(const CArtefact* artefact, bool on_belt);
virtual void OnPlayHeadShotParticle(NET_Packet P);
virtual void Die(IGameObject* who);
virtual void Hit(SHit* pHDS);
virtual void PHHit(SHit& H);
virtual void HitSignal(float P, Fvector& vLocalDir, IGameObject* who, s16 element);
void HitSector(IGameObject* who, IGameObject* weapon);
void HitMark(float P, Fvector dir, IGameObject* who, s16 element, Fvector position_in_bone_space, float impulse,
ALife::EHitType hit_type);
void Feel_Grenade_Update(float rad);
virtual float GetMass();
virtual float Radius() const;
virtual void g_PerformDrop();
virtual bool use_default_throw_force();
virtual float missile_throw_force();
virtual bool unlimited_ammo();
virtual bool NeedToDestroyObject() const;
virtual ALife::_TIME_ID TimePassedAfterDeath() const;
public:
//свойства артефактов
virtual void UpdateArtefactsOnBeltAndOutfit();
float HitArtefactsOnBelt(float hit_power, ALife::EHitType hit_type);
float GetProtection_ArtefactsOnBelt(ALife::EHitType hit_type);
protected:
//звук тяжелого дыхания
ref_sound m_HeavyBreathSnd;
ref_sound m_BloodSnd;
ref_sound m_DangerSnd;
xr_vector<const CArtefact*> m_ArtefactsOnBelt;
protected:
// Death
float m_hit_slowmo;
float m_hit_probability;
s8 m_block_sprint_counter;
// media
SndShockEffector* m_sndShockEffector;
xr_vector<ref_sound> sndHit[ALife::eHitTypeMax];
ref_sound sndDie[SND_DIE_COUNT];
float m_fLandingTime;
float m_fJumpTime;
float m_fFallTime;
float m_fCamHeightFactor;
// Dropping
BOOL b_DropActivated;
float f_DropPower;
// random seed для Zoom mode
s32 m_ZoomRndSeed;
// random seed для Weapon Effector Shot
s32 m_ShotRndSeed;
bool m_bOutBorder;
//сохраняет счетчик объектов в feel_touch, для которых необходимо обновлять размер колижена с актером
u32 m_feel_touch_characters;
private:
void SwitchOutBorder(bool new_border_state);
public:
bool m_bAllowDeathRemove;
void SetZoomRndSeed(s32 Seed = 0);
s32 GetZoomRndSeed() { return m_ZoomRndSeed; };
void SetShotRndSeed(s32 Seed = 0);
s32 GetShotRndSeed() { return m_ShotRndSeed; };
public:
void detach_Vehicle();
void steer_Vehicle(float angle);
void attach_Vehicle(CHolderCustom* vehicle);
bool use_MountedWeapon(CHolderCustom* object);
virtual bool can_attach(const CInventoryItem* inventory_item) const;
protected:
CHolderCustom* m_holder;
u16 m_holderID;
bool use_Holder(CHolderCustom* holder);
bool use_Vehicle(CHolderCustom* object);
void ActorUse();
protected:
BOOL m_bAnimTorsoPlayed;
static void AnimTorsoPlayCallBack(CBlend* B);
// Rotation
SRotation r_torso;
float r_torso_tgt_roll;
//положение торса без воздействия эффекта отдачи оружия
SRotation unaffected_r_torso;
//ориентация модели
float r_model_yaw_dest;
float r_model_yaw; // orientation of model
float r_model_yaw_delta; // effect on multiple "strafe"+"something"
public:
SActorMotions* m_anims;
SActorVehicleAnims* m_vehicle_anims;
CBlend* m_current_legs_blend;
CBlend* m_current_torso_blend;
CBlend* m_current_jump_blend;
MotionID m_current_legs;
MotionID m_current_torso;
MotionID m_current_head;
// callback на анимации модели актера
void SetCallbacks();
void ResetCallbacks();
static void Spin0Callback(CBoneInstance*);
static void Spin1Callback(CBoneInstance*);
static void ShoulderCallback(CBoneInstance*);
static void HeadCallback(CBoneInstance*);
static void VehicleHeadCallback(CBoneInstance*);
virtual const SRotation Orientation() const { return r_torso; };
SRotation& Orientation() { return r_torso; };
void g_SetAnimation(u32 mstate_rl);
void g_SetSprintAnimation(u32 mstate_rl, MotionID& head, MotionID& torso, MotionID& legs);
public:
void OnHUDDraw(CCustomHUD* hud, IRenderable* root) override;
BOOL HUDview() const;
// visiblity
virtual float ffGetFov() const { return 90.f; }
virtual float ffGetRange() const { return 500.f; }
public:
CActorCameraManager& Cameras()
{
VERIFY(m_pActorEffector);
return *m_pActorEffector;
}
IC CCameraBase* cam_Active() { return cameras[cam_active]; }
IC CCameraBase* cam_FirstEye() { return cameras[eacFirstEye]; }
EActorCameras active_cam() const { return cam_active; } // KD: we need to know which cam is active outside actor methods
virtual void cam_Set(EActorCameras style); //Alundaio: made public
protected:
//virtual void cam_Set(EActorCameras style);
void cam_Update(float dt, float fFOV);
void cam_Lookout(const Fmatrix& xform, float camera_height);
void camUpdateLadder(float dt);
void cam_SetLadder();
void cam_UnsetLadder();
float currentFOV();
// Cameras
CCameraBase* cameras[eacMaxCam];
EActorCameras cam_active;
float fPrevCamPos;
float current_ik_cam_shift;
Fvector vPrevCamDir;
float fCurAVelocity;
CEffectorBobbing* pCamBobbing;
//менеджер эффекторов, есть у каждого актрера
CActorCameraManager* m_pActorEffector;
static float f_Ladder_cam_limit;
public:
virtual void feel_touch_new(IGameObject* O);
virtual void feel_touch_delete(IGameObject* O);
virtual bool feel_touch_contact(IGameObject* O);
virtual bool feel_touch_on_contact(IGameObject* O);
CGameObject* ObjectWeLookingAt() { return m_pObjectWeLookingAt; }
CInventoryOwner* PersonWeLookingAt() { return m_pPersonWeLookingAt; }
LPCSTR GetDefaultActionForObject() { return *m_sDefaultObjAction; }
protected:
CGameObject* m_pUsableObject;
// Person we're looking at
CInventoryOwner* m_pPersonWeLookingAt;
CHolderCustom* m_pVehicleWeLookingAt;
CGameObject* m_pObjectWeLookingAt;
CInventoryBox* m_pInvBoxWeLookingAt;
// Tip for action for object we're looking at
shared_str m_sDefaultObjAction;
shared_str m_sCharacterUseAction;
shared_str m_sDeadCharacterUseAction;
shared_str m_sDeadCharacterUseOrDragAction;
shared_str m_sDeadCharacterDontUseAction;
shared_str m_sCarCharacterUseAction;
shared_str m_sInventoryItemUseAction;
shared_str m_sInventoryBoxUseAction;
// shared_str m_quick_use_slots[4];
//режим подбирания предметов
bool m_bPickupMode;
//расстояние (в метрах) на котором актер чувствует гранату (любую)
float m_fFeelGrenadeRadius;
float m_fFeelGrenadeTime; //время гранаты (сек) после которого актер чувствует гранату
//расстояние подсветки предметов
float m_fPickupInfoRadius;
void PickupModeUpdate();
void PickupInfoDraw(IGameObject* object);
void PickupModeUpdate_COD();
//////////////////////////////////////////////////////////////////////////
// Motions (передвижения актрера)
//////////////////////////////////////////////////////////////////////////
public:
void g_cl_CheckControls(u32 mstate_wf, Fvector& vControlAccel, float& Jump, float dt);
void g_cl_ValidateMState(float dt, u32 mstate_wf);
void g_cl_Orientate(u32 mstate_rl, float dt);
void g_sv_Orientate(u32 mstate_rl, float dt);
void g_Orientate(u32 mstate_rl, float dt);
bool g_LadderOrient();
void UpdateMotionIcon(u32 mstate_rl);
bool CanAccelerate();
bool CanJump();
bool CanMove();
float CameraHeight();
// Alex ADD: for smooth crouch fix
float CurrentHeight;
bool CanSprint();
bool CanRun();
void StopAnyMove();
bool AnyAction() { return (mstate_real & mcAnyAction) != 0; };
bool AnyMove() { return (mstate_real & mcAnyMove) != 0; };
bool is_jump();
u32 MovingState() const { return mstate_real; }
protected:
u32 mstate_wishful;
u32 mstate_old;
u32 mstate_real;
BOOL m_bJumpKeyPressed;
public:
float m_fWalkAccel;
float m_fJumpSpeed;
float m_fRunFactor;
float m_fRunBackFactor;
float m_fWalkBackFactor;
float m_fCrouchFactor;
float m_fClimbFactor;
float m_fSprintFactor;
float m_fWalk_StrafeFactor;
float m_fRun_StrafeFactor;
public:
Fvector GetMovementSpeed() { return NET_SavedAccel; };
//////////////////////////////////////////////////////////////////////////
// User input/output
//////////////////////////////////////////////////////////////////////////
public:
virtual void IR_OnMouseMove(int x, int y);
virtual void IR_OnKeyboardPress(int dik);
virtual void IR_OnKeyboardRelease(int dik);
virtual void IR_OnKeyboardHold(int dik);
virtual void IR_OnMouseWheel(int x, int y);
virtual float GetLookFactor();
public:
virtual void g_WeaponBones(int& L, int& R1, int& R2);
virtual void g_fireParams(const CHudItem* pHudItem, Fvector& P, Fvector& D);
virtual bool g_stateFire() { return !((mstate_wishful & mcLookout) && !IsGameTypeSingle()); }
virtual BOOL g_State(SEntityState& state) const;
virtual float GetWeaponAccuracy() const;
float GetFireDispertion() const { return m_fdisp_controller.GetCurrentDispertion(); }
bool IsZoomAimingMode() const { return m_bZoomAimingMode; }
virtual float MaxCarryWeight() const;
float MaxWalkWeight() const;
float get_additional_weight() const;
protected:
CFireDispertionController m_fdisp_controller;
//если актер целится в прицел
void SetZoomAimingMode(bool val) { m_bZoomAimingMode = val; }
bool m_bZoomAimingMode;
//настройки аккуратности стрельбы
//базовая дисперсия (когда игрок стоит на месте)
float m_fDispBase;
float m_fDispAim;
//коэффициенты на сколько процентов увеличится базовая дисперсия
//учитывает скорость актера
float m_fDispVelFactor;
//если актер бежит
float m_fDispAccelFactor;
//если актер сидит
float m_fDispCrouchFactor;
// crouch+no acceleration
float m_fDispCrouchNoAccelFactor;
Fvector m_vMissileOffset;
public:
// Получение, и запись смещения для гранат
Fvector GetMissileOffset() const;
void SetMissileOffset(const Fvector& vNewOffset);
protected:
//косточки используемые при стрельбе
int m_r_hand;
int m_l_finger1;
int m_r_finger2;
int m_head;
int m_eye_left;
int m_eye_right;
int m_l_clavicle;
int m_r_clavicle;
int m_spine2;
int m_spine1;
int m_spine;
int m_neck;
//////////////////////////////////////////////////////////////////////////
// Network
//////////////////////////////////////////////////////////////////////////
void ConvState(u32 mstate_rl, string128* buf);
public:
virtual bool net_Spawn(CSE_Abstract* DC);
virtual void net_Export(NET_Packet& P); // export to server
virtual void net_Import(NET_Packet& P); // import from server
virtual void net_Destroy();
virtual bool net_Relevant(); // { return getSVU() | getLocal(); }; // relevant for export to server
virtual void net_Relcase(IGameObject* O); //
virtual void xr_stdcall on_requested_spawn(IGameObject* object);
// object serialization
virtual void save(NET_Packet& output_packet);
virtual void load(IReader& input_packet);
virtual void net_Save(NET_Packet& P);
virtual bool net_SaveRelevant();
protected:
xr_deque<net_update> NET;
Fvector NET_SavedAccel;
net_update NET_Last;
BOOL NET_WasInterpolating; // previous update was by interpolation or by extrapolation
u32 NET_Time; // server time of last update
//---------------------------------------------
void net_Import_Base(NET_Packet& P);
void net_Import_Physic(NET_Packet& P);
void net_Import_Base_proceed();
void net_Import_Physic_proceed();
//---------------------------------------------
////////////////////////////////////////////////////////////////////////////
virtual bool can_validate_position_on_spawn() { return false; }
///////////////////////////////////////////////////////
// апдайт с данными физики
xr_deque<net_update_A> NET_A;
//---------------------------------------------
// bool m_bHasUpdate;
/// spline coeff /////////////////////
float SCoeff[3][4]; //коэффициэнты для сплайна Бизье
float HCoeff[3][4]; //коэффициэнты для сплайна Эрмита
Fvector IPosS, IPosH, IPosL; //положение актера после интерполяции Бизье, Эрмита, линейной
#ifdef DEBUG
using VIS_POSITION = xr_deque<Fvector>;
VIS_POSITION LastPosS;
VIS_POSITION LastPosH;
VIS_POSITION LastPosL;
#endif
SPHNetState LastState;
SPHNetState RecalculatedState;
SPHNetState PredictedState;
InterpData IStart;
InterpData IRec;
InterpData IEnd;
bool m_bInInterpolation;
bool m_bInterpolate;
u32 m_dwIStartTime;
u32 m_dwIEndTime;
u32 m_dwILastUpdateTime;
//---------------------------------------------
using PH_STATES = xr_deque<SPHNetState>;
PH_STATES m_States;
u16 m_u16NumBones;
void net_ExportDeadBody(NET_Packet& P);
//---------------------------------------------
void CalculateInterpolationParams();
//---------------------------------------------
virtual void make_Interpolation();
#ifdef DEBUG
//---------------------------------------------
virtual void OnRender_Network();
//---------------------------------------------
#endif
// Igor ref_geom hFriendlyIndicator;
//////////////////////////////////////////////////////////////////////////
// Actor physics
//////////////////////////////////////////////////////////////////////////
public:
void g_Physics(Fvector& accel, float jump, float dt);
virtual void ForceTransform(const Fmatrix& m);
void ForceTransformAndDirection(const Fmatrix& m) override;
void SetPhPosition(const Fmatrix& pos);
virtual void PH_B_CrPr(); // actions & operations before physic correction-prediction steps
virtual void PH_I_CrPr(); // actions & operations after correction before prediction steps
virtual void PH_A_CrPr(); // actions & operations after phisic correction-prediction steps
// virtual void UpdatePosStack ( u32 Time0, u32 Time1 );
virtual void MoveActor(Fvector NewPos, Fvector NewDir);
virtual void SpawnAmmoForWeapon(CInventoryItem* pIItem);
virtual void RemoveAmmoForWeapon(CInventoryItem* pIItem);
virtual void spawn_supplies();
virtual bool human_being() const { return (true); }
virtual shared_str GetDefaultVisualOutfit() const { return m_DefaultVisualOutfit; };
virtual void SetDefaultVisualOutfit(shared_str DefaultOutfit) { m_DefaultVisualOutfit = DefaultOutfit; };
virtual void UpdateAnimation() { g_SetAnimation(mstate_real); };
virtual void ChangeVisual(shared_str NewVisual);
virtual void OnChangeVisual();
virtual void RenderIndicator(Fvector dpos, float r1, float r2, const ui_shader& IndShader);
virtual void RenderText(LPCSTR Text, Fvector dpos, float* pdup, u32 color);
//////////////////////////////////////////////////////////////////////////
// Controlled Routines
//////////////////////////////////////////////////////////////////////////
void set_input_external_handler(CActorInputHandler* handler);
bool input_external_handler_installed() const { return (m_input_external_handler != 0); }
IC void lock_accel_for(u32 time) { m_time_lock_accel = Device.dwTimeGlobal + time; }
private:
CActorInputHandler* m_input_external_handler;
u32 m_time_lock_accel;
/////////////////////////////////////////
// DEBUG INFO
protected:
CStatGraph* pStatGraph;
shared_str m_DefaultVisualOutfit;
LPCSTR invincibility_fire_shield_3rd;
LPCSTR invincibility_fire_shield_1st;
shared_str m_sHeadShotParticle;
u32 last_hit_frame;
#ifdef DEBUG
friend class CLevelGraph;
#endif
Fvector m_AutoPickUp_AABB;
Fvector m_AutoPickUp_AABB_Offset;
void Check_for_AutoPickUp();
void SelectBestWeapon(IGameObject* O);
public:
void SetWeaponHideState(u16 State, bool bSet);
private: // IPhysicsShellHolder
virtual void HideAllWeapons(bool v) { SetWeaponHideState(INV_STATE_BLOCK_ALL, v); }
public:
void SetCantRunState(bool bSet);
private:
CActorCondition* m_entity_condition;
protected:
virtual CEntityConditionSimple* create_entity_condition(CEntityConditionSimple* ec);
public:
IC CActorCondition& conditions() const;
virtual IFactoryObject* _construct();
virtual bool natural_weapon() const { return false; }
virtual bool natural_detector() const { return false; }
virtual bool use_center_to_aim() const;
protected:
u16 m_iLastHitterID;
u16 m_iLastHittingWeaponID;
s16 m_s16LastHittedElement;
Fvector m_vLastHitDir;
Fvector m_vLastHitPos;
float m_fLastHealth;
bool m_bWasHitted;
bool m_bWasBackStabbed;
virtual bool Check_for_BackStab_Bone(u16 element);
public:
virtual void SetHitInfo(IGameObject* who, IGameObject* weapon, s16 element, Fvector Pos, Fvector Dir);
virtual void OnHitHealthLoss(float NewHealth);
virtual void OnCriticalHitHealthLoss();
virtual void OnCriticalWoundHealthLoss();
virtual void OnCriticalRadiationHealthLoss();
virtual bool InventoryAllowSprint();
virtual void OnNextWeaponSlot();
virtual void OnPrevWeaponSlot();
void SwitchNightVision();
void SwitchTorch();
#ifdef DEBUG
void NoClipFly(int cmd);
#endif // DEBUG
public:
virtual void on_weapon_shot_start(CWeapon* weapon);
virtual void on_weapon_shot_update();
virtual void on_weapon_shot_stop();
virtual void on_weapon_shot_remove(CWeapon* weapon);
virtual void on_weapon_hide(CWeapon* weapon);
Fvector weapon_recoil_delta_angle();
Fvector weapon_recoil_last_delta();
protected:
virtual void update_camera(CCameraShotEffector* effector);
// step manager
virtual bool is_on_ground();
private:
CActorMemory* m_memory;
public:
IC CActorMemory& memory() const
{
VERIFY(m_memory);
return (*m_memory);
};
void OnDifficultyChanged();
IC float HitProbability() { return m_hit_probability; }
virtual CVisualMemoryManager* visual_memory() const;
virtual bool BonePassBullet(int boneID);
virtual void On_B_NotCurrentEntity();
private:
collide::rq_results RQR;
BOOL CanPickItem(const CFrustum& frustum, const Fvector& from, IGameObject* item);
xr_vector<ISpatial*> ISpatialResult;
private:
CLocationManager* m_location_manager;
public:
IC const CLocationManager& locations() const
{
VERIFY(m_location_manager);
return (*m_location_manager);
}
private:
ALife::_OBJECT_ID m_holder_id;
public:
virtual bool register_schedule() const { return false; }
virtual bool is_ai_obstacle() const;
float GetRestoreSpeed(ALife::EConditionRestoreType const& type);
public:
virtual void On_SetEntity();
virtual void On_LostEntity(){};
void DisableHitMarks(bool disable) { m_disabled_hitmarks = disable; };
bool DisableHitMarks() { return m_disabled_hitmarks; };
void set_inventory_disabled(bool is_disabled) { m_inventory_disabled = is_disabled; }
bool inventory_disabled() const { return m_inventory_disabled; }
private:
void set_state_box(u32 mstate);
private:
bool m_disabled_hitmarks;
bool m_inventory_disabled;
// static CPhysicsShell *actor_camera_shell;
IC u32 get_state() const
{
return this->mstate_real;
}
IC void set_state(u32 state)
{
mstate_real = state;
}
IC u32 get_state_wishful() const
{
return this->mstate_wishful;
}
IC void set_state_wishful(u32 state)
{
mstate_wishful = state;
}
};
This code is from re3, an opensource reverse engineered rewrite of the GTA III engine.
class CPlaceable
{
public:
// disable allocation
static void *operator new(size_t);
CMatrix m_matrix;
CPlaceable(void);
virtual ~CPlaceable(void);
const CVector &GetPosition(void) { return m_matrix.GetPosition(); }
void SetPosition(float x, float y, float z) {
m_matrix.GetPosition().x = x;
m_matrix.GetPosition().y = y;
m_matrix.GetPosition().z = z;
}
void SetPosition(const CVector &pos) { m_matrix.GetPosition() = pos; }
CVector &GetRight(void) { return m_matrix.GetRight(); }
CVector &GetForward(void) { return m_matrix.GetForward(); }
CVector &GetUp(void) { return m_matrix.GetUp(); }
CMatrix &GetMatrix(void) { return m_matrix; }
void SetTransform(RwMatrix *m) { m_matrix = CMatrix(m, false); }
void SetHeading(float angle);
void SetOrientation(float x, float y, float z){
CVector pos = m_matrix.GetPosition();
m_matrix.SetRotate(x, y, z);
m_matrix.Translate(pos);
}
bool IsWithinArea(float x1, float y1, float x2, float y2);
bool IsWithinArea(float x1, float y1, float z1, float x2, float y2, float z2);
};
class CPhysical : public CEntity
{
public:
// The not properly indented fields haven't been checked properly yet
int32 m_audioEntityId;
float m_phys_unused1;
CTreadable *m_treadable[2]; // car and ped
uint32 m_nLastTimeCollided;
CVector m_vecMoveSpeed; // velocity
CVector m_vecTurnSpeed; // angular velocity
CVector m_vecMoveFriction;
CVector m_vecTurnFriction;
CVector m_vecMoveSpeedAvg;
CVector m_vecTurnSpeedAvg;
float m_fMass;
float m_fTurnMass; // moment of inertia
float m_fForceMultiplier;
float m_fAirResistance;
float m_fElasticity;
float m_fBuoyancy;
CVector m_vecCentreOfMass;
CEntryInfoList m_entryInfoList;
CPtrNode *m_movingListNode;
int8 m_phys_unused2;
uint8 m_nStaticFrames;
uint8 m_nCollisionRecords;
bool m_bIsVehicleBeingShifted;
CEntity *m_aCollisionRecords[PHYSICAL_MAX_COLLISIONRECORDS];
float m_fDistanceTravelled;
// damaged piece
float m_fDamageImpulse;
CEntity *m_pDamageEntity;
CVector m_vecDamageNormal;
int16 m_nDamagePieceType;
uint8 bIsHeavy : 1;
uint8 bAffectedByGravity : 1;
uint8 bInfiniteMass : 1;
uint8 bIsInWater : 1;
uint8 m_phy_flagA10 : 1; // unused
uint8 m_phy_flagA20 : 1; // unused
uint8 bHitByTrain : 1;
uint8 bSkipLineCol : 1;
uint8 m_nSurfaceTouched;
int8 m_nZoneLevel;
CPhysical(void);
~CPhysical(void);
// from CEntity
void Add(void);
void Remove(void);
CRect GetBoundRect(void);
void ProcessControl(void);
void ProcessShift(void);
void ProcessCollision(void);
virtual int32 ProcessEntityCollision(CEntity *ent, CColPoint *colpoints);
void RemoveAndAdd(void);
void AddToMovingList(void);
void RemoveFromMovingList(void);
void SetDamagedPieceRecord(uint16 piece, float impulse, CEntity *entity, CVector dir);
void AddCollisionRecord(CEntity *ent);
void AddCollisionRecord_Treadable(CEntity *ent);
bool GetHasCollidedWith(CEntity *ent);
void RemoveRefsToEntity(CEntity *ent);
static void PlacePhysicalRelativeToOtherPhysical(CPhysical *other, CPhysical *phys, CVector localPos);
// get speed of point p relative to entity center
CVector GetSpeed(const CVector &r);
CVector GetSpeed(void) { return GetSpeed(CVector(0.0f, 0.0f, 0.0f)); }
float GetMass(const CVector &pos, const CVector &dir) {
return 1.0f / (CrossProduct(pos, dir).MagnitudeSqr()/m_fTurnMass +
1.0f/m_fMass);
}
float GetMassTweak(const CVector &pos, const CVector &dir, float t) {
return 1.0f / (CrossProduct(pos, dir).MagnitudeSqr()/(m_fTurnMass*t) +
1.0f/(m_fMass*t));
}
void UnsetIsInSafePosition(void) {
m_vecMoveSpeed *= -1.0f;
m_vecTurnSpeed *= -1.0f;
ApplyTurnSpeed();
ApplyMoveSpeed();
m_vecMoveSpeed *= -1.0f;
m_vecTurnSpeed *= -1.0f;
bIsInSafePosition = false;
}
const CVector &GetMoveSpeed() { return m_vecMoveSpeed; }
void SetMoveSpeed(float x, float y, float z) {
m_vecMoveSpeed.x = x;
m_vecMoveSpeed.y = y;
m_vecMoveSpeed.z = z;
}
void SetMoveSpeed(const CVector& speed) {
m_vecMoveSpeed = speed;
}
const CVector &GetTurnSpeed() { return m_vecTurnSpeed; }
void SetTurnSpeed(float x, float y, float z) {
m_vecTurnSpeed.x = x;
m_vecTurnSpeed.y = y;
m_vecTurnSpeed.z = z;
}
const CVector &GetCenterOfMass() { return m_vecCentreOfMass; }
void SetCenterOfMass(float x, float y, float z) {
m_vecCentreOfMass.x = x;
m_vecCentreOfMass.y = y;
m_vecCentreOfMass.z = z;
}
void ApplyMoveSpeed(void);
void ApplyTurnSpeed(void);
// Force actually means Impulse here
void ApplyMoveForce(float jx, float jy, float jz);
void ApplyMoveForce(const CVector &j) { ApplyMoveForce(j.x, j.y, j.z); }
// j(x,y,z) is direction of force, p(x,y,z) is point relative to model center where force is applied
void ApplyTurnForce(float jx, float jy, float jz, float px, float py, float pz);
// j is direction of force, p is point relative to model center where force is applied
void ApplyTurnForce(const CVector &j, const CVector &p) { ApplyTurnForce(j.x, j.y, j.z, p.x, p.y, p.z); }
void ApplyFrictionMoveForce(float jx, float jy, float jz);
void ApplyFrictionMoveForce(const CVector &j) { ApplyFrictionMoveForce(j.x, j.y, j.z); }
void ApplyFrictionTurnForce(float jx, float jy, float jz, float rx, float ry, float rz);
void ApplyFrictionTurnForce(const CVector &j, const CVector &p) { ApplyFrictionTurnForce(j.x, j.y, j.z, p.x, p.y, p.z); }
// springRatio: 1.0 fully extended, 0.0 fully compressed
bool ApplySpringCollision(float springConst, CVector &springDir, CVector &point, float springRatio, float bias);
bool ApplySpringDampening(float damping, CVector &springDir, CVector &point, CVector &speed);
void ApplyGravity(void);
void ApplyFriction(void);
void ApplyAirResistance(void);
bool ApplyCollision(CPhysical *B, CColPoint &colpoint, float &impulseA, float &impulseB);
bool ApplyCollisionAlt(CEntity *B, CColPoint &colpoint, float &impulse, CVector &moveSpeed, CVector &turnSpeed);
bool ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint);
bool ApplyFriction(float adhesiveLimit, CColPoint &colpoint);
bool ProcessShiftSectorList(CPtrList *ptrlists);
bool ProcessCollisionSectorList_SimpleCar(CPtrList *lists);
bool ProcessCollisionSectorList(CPtrList *lists);
bool CheckCollision(void);
bool CheckCollision_SimpleCar(void);
};
class CEntity : public CPlaceable
{
public:
RwObject *m_rwObject;
protected:
uint32 m_type : 3;
private:
uint32 m_status : 5;
public:
// flagsA
uint32 bUsesCollision : 1; // does entity use collision
uint32 bCollisionProcessed : 1; // has object been processed by a ProcessEntityCollision function
uint32 bIsStatic : 1; // is entity static
uint32 bHasContacted : 1; // has entity processed some contact forces
uint32 bPedPhysics : 1;
uint32 bIsStuck : 1; // is entity stuck
uint32 bIsInSafePosition : 1; // is entity in a collision free safe position
uint32 bUseCollisionRecords : 1;
// flagsB
uint32 bWasPostponed : 1; // was entity control processing postponed
uint32 bExplosionProof : 1;
uint32 bIsVisible : 1; //is the entity visible
uint32 bHasCollided : 1;
uint32 bRenderScorched : 1;
uint32 bHasBlip : 1;
uint32 bIsBIGBuilding : 1; // Set if this entity is a big building
uint32 bRenderDamaged : 1; // use damaged LOD models for objects with applicable damage
// flagsC
uint32 bBulletProof : 1;
uint32 bFireProof : 1;
uint32 bCollisionProof : 1;
uint32 bMeleeProof : 1;
uint32 bOnlyDamagedByPlayer : 1;
uint32 bStreamingDontDelete : 1; // Dont let the streaming remove this
uint32 bZoneCulled : 1;
uint32 bZoneCulled2 : 1; // only treadables+10m
// flagsD
uint32 bRemoveFromWorld : 1; // remove this entity next time it should be processed
uint32 bHasHitWall : 1; // has collided with a building (changes subsequent collisions)
uint32 bImBeingRendered : 1; // don't delete me because I'm being rendered
uint32 bTouchingWater : 1; // used by cBuoyancy::ProcessBuoyancy
uint32 bIsSubway : 1; // set when subway, but maybe different meaning?
uint32 bDrawLast : 1; // draw object last
uint32 bNoBrightHeadLights : 1;
uint32 bDoNotRender : 1;
// flagsE
uint32 bDistanceFade : 1; // Fade entity because it is far away
uint32 m_flagE2 : 1;
uint16 m_scanCode;
uint16 m_randomSeed;
int16 m_modelIndex;
uint16 m_level; // int16
CReference *m_pFirstReference;
public:
uint8 GetType() const { return m_type; }
void SetType(uint8 type) { m_type = type; }
uint8 GetStatus() const { return m_status; }
void SetStatus(uint8 status) { m_status = status; }
CColModel *GetColModel(void) { return CModelInfo::GetModelInfo(m_modelIndex)->GetColModel(); }
bool GetIsStatic(void) const { return bIsStatic; }
void SetIsStatic(bool state) { bIsStatic = state; }
#ifdef COMPATIBLE_SAVES
void SaveEntityFlags(uint8*& buf);
void LoadEntityFlags(uint8*& buf);
#else
uint32* GetAddressOfEntityProperties() { /* AWFUL */ return (uint32*)((char*)&m_rwObject + sizeof(m_rwObject)); }
#endif
CEntity(void);
~CEntity(void);
virtual void Add(void);
virtual void Remove(void);
virtual void SetModelIndex(uint32 id);
virtual void SetModelIndexNoCreate(uint32 id);
virtual void CreateRwObject(void);
virtual void DeleteRwObject(void);
virtual CRect GetBoundRect(void);
virtual void ProcessControl(void) {}
virtual void ProcessCollision(void) {}
virtual void ProcessShift(void) {}
virtual void Teleport(CVector v) {}
virtual void PreRender(void);
virtual void Render(void);
virtual bool SetupLighting(void);
virtual void RemoveLighting(bool);
virtual void FlagToDestroyWhenNextProcessed(void) {}
bool IsBuilding(void) { return m_type == ENTITY_TYPE_BUILDING; }
bool IsVehicle(void) { return m_type == ENTITY_TYPE_VEHICLE; }
bool IsPed(void) { return m_type == ENTITY_TYPE_PED; }
bool IsObject(void) { return m_type == ENTITY_TYPE_OBJECT; }
bool IsDummy(void) { return m_type == ENTITY_TYPE_DUMMY; }
RpAtomic *GetAtomic(void) {
assert(RwObjectGetType(m_rwObject) == rpATOMIC);
return (RpAtomic*)m_rwObject;
}
RpClump *GetClump(void) {
assert(RwObjectGetType(m_rwObject) == rpCLUMP);
return (RpClump*)m_rwObject;
}
void GetBoundCentre(CVector &out);
CVector GetBoundCentre(void);
float GetBoundRadius(void);
float GetDistanceFromCentreOfMassToBaseOfModel(void);
bool GetIsTouching(CVector const ¢er, float r);
bool GetIsOnScreen(void);
bool GetIsOnScreenComplex(void);
bool IsVisible(void);
bool IsVisibleComplex(void);
int16 GetModelIndex(void) const { return m_modelIndex; }
void UpdateRwFrame(void);
void SetupBigBuilding(void);
void AttachToRwObject(RwObject *obj);
void DetachFromRwObject(void);
void RegisterReference(CEntity **pent);
void ResolveReferences(void);
void PruneReferences(void);
#ifdef PED_SKIN
void UpdateRpHAnim(void);
#endif
void PreRenderForGlassWindow(void);
void AddSteamsFromGround(CVector *unused);
void ModifyMatrixForTreeInWind(void);
void ModifyMatrixForBannerInWind(void);
void ProcessLightsForEntity(void);
};
class CObject : public CPhysical
{
public:
CMatrix m_objectMatrix;
float m_fUprootLimit;
int8 ObjectCreatedBy;
int8 bIsPickup : 1;
int8 bPickupObjWithMessage : 1;
int8 bOutOfStock : 1;
int8 bGlassCracked : 1;
int8 bGlassBroken : 1;
int8 bHasBeenDamaged : 1;
int8 bUseVehicleColours : 1;
int8 m_nBonusValue;
float m_fCollisionDamageMultiplier;
uint8 m_nCollisionDamageEffect;
uint8 m_nSpecialCollisionResponseCases;
bool m_bCameraToAvoidThisObject;
uint32 m_obj_unused1;
uint32 m_nEndOfLifeTime;
int16 m_nRefModelIndex;
CEntity *m_pCurSurface;
CEntity *m_pCollidingEntity;
int8 m_colour1, m_colour2;
static int16 nNoTempObjects;
static int16 nBodyCastHealth;
static void *operator new(size_t);
static void *operator new(size_t, int);
static void operator delete(void*, size_t);
static void operator delete(void*, int);
CObject(void);
CObject(int32, bool);
CObject(CDummyObject*);
~CObject(void);
void ProcessControl(void);
void Teleport(CVector vecPos);
void Render(void);
bool SetupLighting(void);
void RemoveLighting(bool reset);
void ObjectDamage(float amount);
void RefModelInfo(int32 modelId);
void Init(void);
bool CanBeDeleted(void);
static void DeleteAllMissionObjects();
static void DeleteAllTempObjects();
static void DeleteAllTempObjectsInArea(CVector point, float fRadius);
};
This is code from an opensource rewrite of the The Elder Scrolls: Morrowind engine. It is a full rewrite, unlike many others here, and the structures likely don't match as closely the original ones.
/// \brief Class holding functionality common to Creature and NPC
class Actor : public MWWorld::Class
{
protected:
Actor();
public:
virtual ~Actor();
void adjustPosition(const MWWorld::Ptr& ptr, bool force) const override;
///< Adjust position to stand on ground. Must be called post model load
/// @param force do this even if the ptr is flying
void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const override;
bool useAnim() const override;
void block(const MWWorld::Ptr &ptr) const override;
osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const override;
///< Return desired rotations, as euler angles. Sets getMovementSettings(ptr).mRotation to zero.
float getEncumbrance(const MWWorld::Ptr& ptr) const override;
///< Returns total weight of objects inside this object (including modifications from magic
/// effects). Throws an exception, if the object can't hold other objects.
bool allowTelekinesis(const MWWorld::ConstPtr& ptr) const override;
///< Return whether this class of object can be activated with telekinesis
bool isActor() const override;
/// Return current movement speed.
float getCurrentSpeed(const MWWorld::Ptr& ptr) const override;
// not implemented
Actor(const Actor&);
Actor& operator= (const Actor&);
};
/// @brief Holds temporary state for an actor that will be discarded when the actor leaves the scene.
class Actor
{
public:
Actor(const MWWorld::Ptr& ptr, MWRender::Animation* animation);
/// Notify this actor of its new base object Ptr, use when the object changed cells
void updatePtr(const MWWorld::Ptr& newPtr);
CharacterController* getCharacterController();
int getGreetingTimer() const;
void setGreetingTimer(int timer);
float getAngleToPlayer() const;
void setAngleToPlayer(float angle);
GreetingState getGreetingState() const;
void setGreetingState(GreetingState state);
bool isTurningToPlayer() const;
void setTurningToPlayer(bool turning);
Misc::TimerStatus updateEngageCombatTimer(float duration)
{
return mEngageCombat.update(duration);
}
private:
std::unique_ptr<CharacterController> mCharacterController;
int mGreetingTimer{0};
float mTargetAngleRadians{0.f};
GreetingState mGreetingState{Greet_None};
bool mIsTurningToPlayer{false};
Misc::DeviatingPeriodicTimer mEngageCombat{1.0f, 0.25f, Misc::Rng::deviate(0, 0.25f)};
};
class Objects
{
typedef std::map<MWWorld::Ptr,CharacterController*> PtrControllerMap;
PtrControllerMap mObjects;
public:
Objects();
~Objects();
void addObject (const MWWorld::Ptr& ptr);
///< Register an animated object
void removeObject (const MWWorld::Ptr& ptr);
///< Deregister an object
void updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr& ptr);
///< Updates an object with a new Ptr
void dropObjects(const MWWorld::CellStore *cellStore);
///< Deregister all objects in the given cell.
void update(float duration, bool paused);
///< Update object animations
bool onOpen(const MWWorld::Ptr& ptr);
void onClose(const MWWorld::Ptr& ptr);
bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false);
void skipAnimation(const MWWorld::Ptr& ptr);
void persistAnimationStates();
void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out);
std::size_t size() const
{
return mObjects.size();
}
};
class Actor final : public PtrHolder
{
public:
Actor(const MWWorld::Ptr& ptr, const Resource::BulletShape* shape, PhysicsTaskScheduler* scheduler);
~Actor() override;
/**
* Sets the collisionMode for this actor. If disabled, the actor can fly and clip geometry.
*/
void enableCollisionMode(bool collision);
bool getCollisionMode() const
{
return mInternalCollisionMode.load(std::memory_order_acquire);
}
btConvexShape* getConvexShape() const { return mConvexShape; }
/**
* Enables or disables the *external* collision body. If disabled, other actors will not collide with this actor.
*/
void enableCollisionBody(bool collision);
void updateScale();
void updateRotation();
/**
* Return true if the collision shape looks the same no matter how its Z rotated.
*/
bool isRotationallyInvariant() const;
/**
* Used by the physics simulation to store the simulation result. Used in conjunction with mWorldPosition
* to account for e.g. scripted movements
*/
void setSimulationPosition(const osg::Vec3f& position);
osg::Vec3f getSimulationPosition() const;
void updateCollisionObjectPosition();
/**
* Returns the half extents of the collision body (scaled according to collision scale)
*/
osg::Vec3f getHalfExtents() const;
/**
* Returns the half extents of the collision body (not scaled)
*/
osg::Vec3f getOriginalHalfExtents() const;
/// Returns the mesh translation, scaled and rotated as necessary
osg::Vec3f getScaledMeshTranslation() const;
/**
* Returns the position of the collision body
* @note The collision shape's origin is in its center, so the position returned can be described as center of the actor collision box in world space.
*/
osg::Vec3f getCollisionObjectPosition() const;
/**
* Store the current position into mPreviousPosition, then move to this position.
* Returns true if the new position is different.
*/
bool setPosition(const osg::Vec3f& position);
// force set actor position to be as in Ptr::RefData
void updatePosition();
// register a position offset that will be applied during simulation.
void adjustPosition(const osg::Vec3f& offset, bool ignoreCollisions);
// apply position offset. Can't be called during simulation
void applyOffsetChange();
osg::Vec3f getPosition() const;
osg::Vec3f getPreviousPosition() const;
/**
* Returns the half extents of the collision body (scaled according to rendering scale)
* @note The reason we need this extra method is because of an inconsistency in MW - NPC race scales aren't applied to the collision shape,
* most likely to make environment collision testing easier. However in some cases (swimming level) we want the actual scale.
*/
osg::Vec3f getRenderingHalfExtents() const;
/**
* Sets the current amount of inertial force (incl. gravity) affecting this physic actor
*/
void setInertialForce(const osg::Vec3f &force);
/**
* Gets the current amount of inertial force (incl. gravity) affecting this physic actor
*/
const osg::Vec3f &getInertialForce() const
{
return mForce;
}
void setOnGround(bool grounded);
bool getOnGround() const
{
return mInternalCollisionMode.load(std::memory_order_acquire) && mOnGround.load(std::memory_order_acquire);
}
void setOnSlope(bool slope);
bool getOnSlope() const
{
return mInternalCollisionMode.load(std::memory_order_acquire) && mOnSlope.load(std::memory_order_acquire);
}
btCollisionObject* getCollisionObject() const
{
return mCollisionObject.get();
}
/// Sets whether this actor should be able to collide with the water surface
void setCanWaterWalk(bool waterWalk);
/// Sets whether this actor has been walking on the water surface in the last frame
void setWalkingOnWater(bool walkingOnWater);
bool isWalkingOnWater() const;
MWWorld::Ptr getStandingOnPtr() const;
void setStandingOnPtr(const MWWorld::Ptr& ptr);
unsigned int getStuckFrames() const
{
return mStuckFrames;
}
void setStuckFrames(unsigned int frames)
{
mStuckFrames = frames;
}
const osg::Vec3f &getLastStuckPosition() const
{
return mLastStuckPosition;
}
void setLastStuckPosition(osg::Vec3f position)
{
mLastStuckPosition = position;
}
bool skipCollisions();
void setVelocity(osg::Vec3f velocity);
osg::Vec3f velocity();
private:
MWWorld::Ptr mStandingOnPtr;
/// Removes then re-adds the collision object to the dynamics world
void updateCollisionMask();
void addCollisionMask(int collisionMask);
int getCollisionMask() const;
bool mCanWaterWalk;
std::atomic<bool> mWalkingOnWater;
bool mRotationallyInvariant;
std::unique_ptr<btCollisionShape> mShape;
btConvexShape* mConvexShape;
std::unique_ptr<btCollisionObject> mCollisionObject;
osg::Vec3f mMeshTranslation;
osg::Vec3f mHalfExtents;
osg::Quat mRotation;
osg::Vec3f mScale;
osg::Vec3f mRenderingScale;
osg::Vec3f mSimulationPosition;
osg::Vec3f mPosition;
osg::Vec3f mPreviousPosition;
osg::Vec3f mPositionOffset;
osg::Vec3f mVelocity;
bool mWorldPositionChanged;
bool mSkipCollisions;
btTransform mLocalTransform;
mutable std::mutex mPositionMutex;
unsigned int mStuckFrames;
osg::Vec3f mLastStuckPosition;
osg::Vec3f mForce;
std::atomic<bool> mOnGround;
std::atomic<bool> mOnSlope;
std::atomic<bool> mInternalCollisionMode;
bool mExternalCollisionMode;
PhysicsTaskScheduler* mTaskScheduler;
Actor(const Actor&);
Actor& operator=(const Actor&);
};
class Object final : public PtrHolder
{
public:
Object(const MWWorld::Ptr& ptr, osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance, int collisionType, PhysicsTaskScheduler* scheduler);
~Object() override;
const Resource::BulletShapeInstance* getShapeInstance() const;
void setScale(float scale);
void setRotation(const btQuaternion& quat);
void setOrigin(const btVector3& vec);
void commitPositionChange();
btCollisionObject* getCollisionObject();
const btCollisionObject* getCollisionObject() const;
btTransform getTransform() const;
/// Return solid flag. Not used by the object itself, true by default.
bool isSolid() const;
void setSolid(bool solid);
bool isAnimated() const;
/// @brief update object shape
/// @return true if shape changed
bool animateCollisionShapes();
private:
std::unique_ptr<btCollisionObject> mCollisionObject;
osg::ref_ptr<Resource::BulletShapeInstance> mShapeInstance;
std::map<int, osg::NodePath> mRecIndexToNodePath;
bool mSolid;
btVector3 mScale;
btTransform mLocalTransform;
bool mScaleUpdatePending;
bool mTransformUpdatePending;
mutable std::mutex mPositionMutex;
PhysicsTaskScheduler* mTaskScheduler;
};
This code is from Devilux, an opensource reverse engnineered version of the original Diablo's engine.
typedef struct ObjDataStruct {
char oload;
char ofindex;
char ominlvl;
char omaxlvl;
char olvltype;
char otheme;
char oquest;
int oAnimFlag;
int oAnimDelay; // Tick length of each frame in the current animation
int oAnimLen; // Number of frames in current animation
int oAnimWidth;
BOOL oSolidFlag;
BOOL oMissFlag;
BOOL oLightFlag;
char oBreak;
char oSelFlag;
BOOL oTrapFlag;
} ObjDataStruct;
typedef struct ObjectStruct {
int _otype;
int _ox;
int _oy;
int _oLight;
int _oAnimFlag;
unsigned char *_oAnimData;
int _oAnimDelay; // Tick length of each frame in the current animation
int _oAnimCnt; // Increases by one each game tick, counting how close we are to _pAnimDelay
int _oAnimLen; // Number of frames in current animation
int _oAnimFrame; // Current frame of animation.
int _oAnimWidth;
int _oAnimWidth2;
BOOL _oDelFlag;
char _oBreak; // check
BOOL _oSolidFlag;
BOOL _oMissFlag;
char _oSelFlag; // check
BOOL _oPreFlag;
BOOL _oTrapFlag;
BOOL _oDoorFlag;
int _olid;
int _oRndSeed;
int _oVar1;
int _oVar2;
int _oVar3;
int _oVar4;
int _oVar5;
int _oVar6;
int _oVar7;
int _oVar8;
} ObjectStruct;
A fool known as Khemist49 actually had his hands on the Starcraft source code, but he opted to return it to Blizzard, where it will remain locked and probably serve of no use to anyone.
We do however have an interesting series of blog posts from one of the original devs giving a glimpse into the code structure:
History tells us that programmers feel compelled to try every feature of their new language during the first project, and so it was with class inheritance in StarCraft. Experienced programmers will shudder when seeing the inheritance chain that was designed for the game’s units:CUnit < CDoodad < CFlingy < CThingy
CThingy objects were sprites that could appear anywhere on the game map, but didn’t move or have behaviors, while CFlingys were used for creating particles; when an explosion occurred several of them would spin off in random directions. CDoodad — after 14 years I think this is the class name — was an uninstantiated class that nevertheless had important behaviors required for proper functioning of derived classes. And CUnit was layered on top of that. The behavior of units was scattered all throughout these various modules, and it required an understanding of each class to be able to accomplish anything.
And beyond the horror of the class hierarchy, the CUnit class itself was an unholy mess defined across multiple header files:
class CUnit ... { #include "header_1.h" #include "header_2.h" #include "header_3.h" #include "header_4.h" };Each of those headers was several hundred lines, leading to an overall class definition that could at best be called amusing.
It wasn’t until many years later that the mantra “favor composition over inheritance” gained credence among programmer-kind, but those who worked on StarCraft learned the hard way much earlier.
He also explains his preference for what he calls intrusive linked lists for certain things, and mentions it being used in the game code, i.e. a list like
struct person {
TLink link; // The "intrusive" link field
unsigned age;
unsigned weight;
};
TListDeclare<person, offsetof(person, link)> people;
instead of
struct person {
unsigned age;
unsigned weight;
};
std::list <person*> people;
Volition, Inc. made the code for FreeSpace / FreeSpace 2 available, although "You may not sell or otherwise commercially exploit the source or things you created based on the source". It is a nice attitude on their part.
typedef struct object {
struct object *next, *prev; // for linked lists of objects
int signature; // Every object ever has a unique signature...
char type; // what type of object this is... robot, weapon, hostage, powerup, fireball
int parent; // This object's parent.
int parent_sig; // This object's parent's signature
char parent_type; // This object's parent's type
int instance; // which instance. ie.. if type is Robot, then this indexes into the Robots array
uint flags; // misc flags. Call obj_set_flags to change this.
vector pos; // absolute x,y,z coordinate of center of object
matrix orient; // orientation of object in world
float radius; // 3d size of object - for collision detection
vector last_pos; // where object was last frame
matrix last_orient; // how the object was oriented last frame
physics_info phys_info; // a physics object
float shields[MAX_SHIELD_SECTIONS]; // Shield is broken into components. Quadrants on 4/24/97.
float hull_strength; // Remaining hull strength.
short objsnd_num[MAX_OBJECT_SOUNDS]; // Index of persistant sound struct. -1 if no persistant sound assigned.
ushort net_signature;
int num_pairs; // How many object pairs this is associated with. When 0 then there are no more.
} object;
// Persistant sounds for objects (pointer to obj_snd is in object struct)
typedef struct _obj_snd {
_obj_snd *next, *prev;
int objnum; // object index of object that contains this sound
int id; // Index into Snds[] array
int instance; // handle of currently playing sound (a ds3d handle if USES_DS3D flag set)
int next_update; // timestamp that marks next allowed vol/pan change
float vol; // volume of sound (range: 0.0 -> 1.0)
float pan; // pan of sound (range: -1.0 -> 1.0)
int freq; // valid range: 100 -> 100000 Hz
int flags;
vector offset; // offset from the center of the object where the sound lives
} obj_snd;
//Data for objects
object Objects[MAX_OBJECTS];
Functions: