Go to main page.
Go to C3 Mod page.
Go to Sol Badguy for OpenBoR page.
This is a listing of changes I've made to the OpenBoR and OpenDarkBoR source codes. Each .rar file contains an executable for the PC which contains the new features, and a .c file called openbor.c which is the modified source. Any changes I've made of any kind will be surrounded by a pair of '//Fugue///'s. So if you're looking for what's different, just do a search for //Fugue///.
None of these are in the latest OpenBoR executable. They are
in the .rars I have here for download. If anyone wants to try out
the features, please download the executable and do so- any
comments, questions, or bug reports would be very helpful.
These were added to the PC version 2.0041
and everything
should work for both players and enemies (except the stage rocking, of
course). I didn't notice any massive slowdown or anything like that,
but I didn't get to test them in a wide variety of situations. I've
been unable to reproduce the random crashing with counterattacks, so I
think I figured out the problem.
Counterattacks:
Additional pain/fall animations:
New rocking animations:
3 will cause the stage to shake with a
constant, steady rumbling, with
occasional 'hiccups'. This one looks like what you might feel if you
were riding in a moving van or on top of a moving eighteen wheeler.
In the ATK_... defines, for the new pain/falls:
<>#define ATK_NORMAL2 6 // Additional attack types for the new pain anims
#define ATK_NORMAL3 7
#define ATK_NORMAL4 8
In the ANI_... defines, for followups, counters, and new pain animations:
#define ANI_FOLLOW1 59 // Multi-purpose animations
#define ANI_FOLLOW2 60
#define ANI_FOLLOW3 61
#define ANI_FOLLOW4 62
#define ANI_PAIN2 63 // Additional pain/fall animations
#define ANI_FALL2 64
#define ANI_PAIN3 65
#define ANI_FALL3 66
#define ANI_PAIN4 67
#define ANI_FALL4 68
#define MAX_ANIS 69 // max_anis increased for new ANIs
In the definition for struct s_anim, for followups and counters:
int followanim; // use which FOLLOW anim?
int followcond; // conditions under which to use a followup
int counterframe; // frame in which a counter is possible
int countercond; // conditions to switch to a FOLLOW anim
int counterdamage; // You can specify if counterattackers take damagein void load_cached_model(char * name), for followups, counters, and new pain animations:
else if(stricmp(value, "follow1")==0){ // 4 followup animations for follow-ups and counters
newchar->animation[ANI_FOLLOW1] = newanim;
}
else if(stricmp(value, "follow2")==0){
newchar->animation[ANI_FOLLOW2] = newanim;
}
else if(stricmp(value, "follow3")==0){
newchar->animation[ANI_FOLLOW3] = newanim;
}
else if(stricmp(value, "follow4")==0){
newchar->animation[ANI_FOLLOW4] = newanim;
}
else if(stricmp(value, "pain2")==0){ // 3 new pairs of pain/fall animations
newchar->animation[ANI_PAIN2] = newanim;
}
else if(stricmp(value, "fall2")==0){
newchar->animation[ANI_FALL2] = newanim;
}
else if(stricmp(value, "pain3")==0){
newchar->animation[ANI_PAIN3] = newanim;
}
else if(stricmp(value, "fall3")==0){
newchar->animation[ANI_FALL3] = newanim;
}
else if(stricmp(value, "pain4")==0){
newchar->animation[ANI_PAIN4] = newanim;
}
else if(stricmp(value, "fall4")==0){
newchar->animation[ANI_FALL4] = newanim;
}
In the reset vars section of the same function, for followups and counters:
newanim->followanim = -1; // Default disabled
newanim->followcond = 0;
newanim->counterframe = -1;
newanim->countercond = 0;
newanim->counterdamage = 0; // Default no damage on counterEven later on in the same function, while reading in animation data, for followups and counters:
else if(stricmp(command, "followanim")==0){ // entities can now use followup attacks
newanim->followanim = atoi(findarg(buf+pos, 1));
if(newanim->followanim > 4) newanim->followanim = 4; // only 4 followups
if(newanim->followanim < 1) newanim->followanim = 1;
}
else if(stricmp(command, "followcond")==0){
newanim->followcond = atoi(findarg(buf+pos, 1));
}
else if(stricmp(command, "counterframe")==0){ // entities can now counterattack
newanim->counterframe = atoi(findarg(buf+pos, 1));
newanim->countercond = atoi(findarg(buf+pos, 2));
newanim->counterdamage = atoi(findarg(buf+pos, 3));
if(newanim->counterframe > 4) newanim->counterframe = 4;
}Still the same function, now while loading the attack data, for additional pain anims:
else if(stricmp(command, "attack2")==0){ // Even more attack types.
attack[0] = atoi(findarg(buf+pos, 1));
attack[1] = atoi(findarg(buf+pos, 2));
attack[2] = atoi(findarg(buf+pos, 3));
attack[3] = atoi(findarg(buf+pos, 4));
attackforce = atoi(findarg(buf+pos, 5));
attackdrop = atoi(findarg(buf+pos, 6));
noblock = atoi(findarg(buf+pos, 7));
noflash = atoi(findarg(buf+pos, 8));
pauseadd = atoi(findarg(buf+pos, 9));
attacktype = ATK_NORMAL2;
}
else if(stricmp(command, "attack3")==0){ // Even more attack types.
attack[0] = atoi(findarg(buf+pos, 1));
attack[1] = atoi(findarg(buf+pos, 2));
attack[2] = atoi(findarg(buf+pos, 3));
attack[3] = atoi(findarg(buf+pos, 4));
attackforce = atoi(findarg(buf+pos, 5));
attackdrop = atoi(findarg(buf+pos, 6));
noblock = atoi(findarg(buf+pos, 7));
noflash = atoi(findarg(buf+pos, 8));
pauseadd = atoi(findarg(buf+pos, 9));
attacktype = ATK_NORMAL3;
}
else if(stricmp(command, "attack4")==0){ // Even more attack types.
attack[0] = atoi(findarg(buf+pos, 1));
attack[1] = atoi(findarg(buf+pos, 2));
attack[2] = atoi(findarg(buf+pos, 3));
attack[3] = atoi(findarg(buf+pos, 4));
attackforce = atoi(findarg(buf+pos, 5));
attackdrop = atoi(findarg(buf+pos, 6));
noblock = atoi(findarg(buf+pos, 7));
noflash = atoi(findarg(buf+pos, 8));
pauseadd = atoi(findarg(buf+pos, 9));
attacktype = ATK_NORMAL4;
}
In the declarations for void do_attack(entity *e), for followups and counters:
int them2; // A stricter definition of "them" for followups
Same function, this should replace the current code used to define 'them':
if(e->projectile > 0) them2 = them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
else if(e->type == TYPE_PLAYER && !savedata.mode){
them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
them2 = TYPE_PLAYER | TYPE_ENEMY;
}
else if(e->type == TYPE_SHOT && !savedata.mode){
them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
them2 = TYPE_PLAYER | TYPE_ENEMY;
}
else if(e->type == TYPE_ENEMY && e->model->subtype == SUBTYPE_ARROW){
them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE; // Added so arrows can damage enemies, obstacles and players
them2 = TYPE_PLAYER;
}
else if(e->model->hitenemy) them2 = them = TYPE_PLAYER | TYPE_ENEMY; // Enemy projectiles can now hit enemies
else if(e->type == TYPE_ENEMY) them2 = them = TYPE_PLAYER;
else if(e->type == TYPE_PLAYER && savedata.mode){
them = TYPE_ENEMY | TYPE_OBSTACLE;
them2 = TYPE_ENEMY;
}
else if(e->type == TYPE_SHOT && savedata.mode){
them = TYPE_ENEMY | TYPE_OBSTACLE;
them2 = TYPE_ENEMY;
}
else if(e->type == TYPE_TRAP) them2 = them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE;
else if(e->type == TYPE_OBSTACLE) them2 = them = TYPE_PLAYER | TYPE_ENEMY | TYPE_OBSTACLE; // Added so obstacles can explode and damage enemies/players/obstacles
else return;
Same function, after spawning a blockflash but before else{ self->takedamage(...), for counters:
// New counterattack code
else if((self->animation->countercond) && // Counterattack?
(self->animation->counterframe == self->animpos) && // is this the right frame?
(!(self->frozen || didblock)) && // check for frozen or blocked attacks
((self->animation->countercond < 2) || (ent_list[i]->type & them2)) && // Does type matter?
((self->animation->countercond < 3) || !(e->animation->no_block[e->animpos] || // Can you counter unblockables?
(self->direction == e->direction) || // How about back attacks?
(e->animation->attack_type[e->animpos] == ATK_FREEZE))) && // And freeze attacks?
(!self->animation->counterdamage || (self->health > force))){ // Does damage matter?
if(self->animation->counterdamage) self->health -= force; // Take damage?
ent_reset_anim(self, ANI_FOLLOW1 + self->animation->followanim - 1);
}
else{ // Didn't block or counter so go ahead and take the hit
Same function, between temp2->type = TYPE_FLASH; and self = temp;, for followups:
// New followup code
if((e->animation->followcond) && // follow up?
((e->animation->followcond < 2) || (ent_list[i]->type & them2)) && // Does type matter?
((e->animation->followcond < 3) || ((self->health > 0) &&
!didblock)) && // check if health or blocking matters
((e->animation->followcond < 4) || !self->model->cantgrab)) // check if nograb matters
ent_set_anim(e, ANI_FOLLOW1 + e->animation->followanim - 1); // Then go to it!
In void player_takedamage(entity *other, int force, int drop, int type), for new fall animations:
else if(type == ATK_NORMAL2 && self->model->animation[ANI_FALL2]) ent_reset_anim(self, ANI_FALL2);
else if(type == ATK_NORMAL3 && self->model->animation[ANI_FALL3]) ent_reset_anim(self, ANI_FALL3);
else if(type == ATK_NORMAL4 && self->model->animation[ANI_FALL4]) ent_reset_anim(self, ANI_FALL4);
Same function, but this time for the pain animations:
else if(type == ATK_NORMAL2 && self->model->animation[ANI_PAIN2]) ent_reset_anim(self, ANI_PAIN2);
else if(type == ATK_NORMAL3 && self->model->animation[ANI_PAIN3]) ent_reset_anim(self, ANI_PAIN3);
else if(type == ATK_NORMAL4 && self->model->animation[ANI_PAIN4]) ent_reset_anim(self, ANI_PAIN4);
In int enemy_trymove(float xdir, float zdir), checking if a throw can be performed, for additional fall animations:
self->animation != self->model->animation[ANI_FALL2] &&
self->animation != self->model->animation[ANI_FALL3] &&
self->animation != self->model->animation[ANI_FALL4] &&
(This code should also be copied into the check for grabbing further down.)
In void enemy_takedamage(entity *other, int force, int drop, int type), for the new fall animations:
else if(type == ATK_NORMAL2 && self->model->animation[ANI_FALL2]) ent_reset_anim(self, ANI_FALL2);
else if(type == ATK_NORMAL3 && self->model->animation[ANI_FALL3]) ent_reset_anim(self, ANI_FALL3);
else if(type == ATK_NORMAL4 && self->model->animation[ANI_FALL4]) ent_reset_anim(self, ANI_FALL4);
Same function, but this time for the pain animations:
else if(type == ATK_NORMAL2 && self->model->animation[ANI_FALL2]) ent_reset_anim(self, ANI_FALL2);
else if(type == ATK_NORMAL3 && self->model->animation[ANI_FALL3]) ent_reset_anim(self, ANI_FALL3);
else if(type == ATK_NORMAL4 && self->model->animation[ANI_FALL4]) ent_reset_anim(self, ANI_FALL4);
Now in void biker_takedamage(entity *other, int force, int drop, int type), for new fall animations:
else if(type == ATK_NORMAL2 && self->model->animation[ANI_FALL2]) ent_reset_anim(self, ANI_FALL2);
else if(type == ATK_NORMAL3 && self->model->animation[ANI_FALL3]) ent_reset_anim(self, ANI_FALL3);
else if(type == ATK_NORMAL4 && self->model->animation[ANI_FALL4]) ent_reset_anim(self, ANI_FALL4);
Now in void draw_scrolled_bg(), for the new rock animations:
static int rockoffssine[32] = {
2, 2, 3, 4, 5, 6, 7, 7,
8, 8, 9, 9, 9, 9, 8, 8,
7, 7, 6, 5, 4, 3, 2, 2,
1, 1, 0, 0, 0, 0, 1, 1
}; // normal, smooth rock
static int rockoffsshake[32] = {
2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 0, 4, 2, 0, 4, 2,
2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 0, 4, 2, 0, 4, 2,
}; // slow, constant jarring rock, like on a train
static int rockoffsrumble[32] = {
2, 2, 3, 3, 2, 2, 3, 3,
2, 2, 3, 3, 2, 3, 2, 3,
2, 2, 3, 3, 2, 2, 3, 3,
2, 2, 3, 3, 2, 3, 2, 3
}; // fast, constant rumbling, like in/on a van or trailer
Same function, after the line rockpos = (time/(GAME_SPEED/8)) & 31;, for the new rocking features:
if(level->rocking == 1)
gfx_y_offset = quake - 4 - rockoffssine[rockpos];
else if(level->rocking == 2)
gfx_y_offset = quake - 4 - rockoffsshake[rockpos];
else if(level->rocking == 3)
gfx_y_offset = quake - 4 - rockoffsrumble[rockpos];
These changes SHOULD all work safely without crashing or breaking
any other features, but any comments/bug reports anyone can give
would be very helpful.
Aggression source and EXE here:
BoR Source/EXE Edits
Again, any comments are appreciated.
In this version, I've only added one new variable, called 'aggression'. It's for enemies only, and can be defined in their .txt file, or in their spawn definitions in levels. It influences how much time they will spend standing around like an idiot before they'll start their own attacks. In other words, the higher this number is, the quicker this enemy will attack, and the lower this number is, the more time this enemy will stand still deciding to attack.
It also currently changes how much time an enemy will take to rise up after being knocked down, and how long they pause after/before moving around. Those changes could be removed easily, though.
The behavior still has a somewhat random element, but this would give some more control to the modder over enemy intelligence.
Negative numbers make the enemy slower, positive make them faster. Using 0 will not change their A.I. at all.
Some examples:
aggression -50
would make an enemy who will attack much more slowly.
aggression 10
would make an enemy who will attack slightly quicker.
aggression 50
would be a very cruel enemy who attacked pretty much as soon as a player got in range of any attacks. Overusing high-aggression enemies saps the fun out of the game pretty fast, but when used in moderation I think it adds a lot.
And now, the code:
in the definition for structs 's_model', 'entity', and 's_spawn_entry':
int aggression; // 01/18/05 New 'control' for enemy A.I.: alters delay before thinking
in void load_cached_model(...), initializing variables:
newchar->aggression = 0; // 01/18/05 New 'control's for enemy A.I.: alters delay before attacking
same function, when reading variables:
else if(stricmp(command, "aggression")==0){
value = findarg(buf+pos, 1);
newchar->aggression = atoi(value);
}
in void load_level(..), reading variables:
else if(stricmp(command, "aggression")==0){ // Aggression can be set per spawn.
next.aggression = atoi(findarg(buf+pos, 1));
}
in entity * spawn(...):
e->aggression = model->aggression; // 01/18/05 New 'control's for enemy A.I.: alters delay before attacking
in void enemy_think(...), after 'self->think = enemy_prepare':
self->stalltime = time + (GAME_SPEED/4) + (rand32()%(GAME_SPEED/10) - self->aggression);
same function, but after deciding a speed and direction for walking (Remove this part to not affect enemy movement)
self->stalltime = time + GAME_SPEED - self->aggression;
in void enemy_attack(...), at the very end:
self->stalltime = time + GAME_SPEED - self->aggression;
in void enemy_fall(...), also at the very end (Remove this part to not affect enemy recovery from a knockdown):
self->stalltime = time + GAME_SPEED - self->aggression;
in entity * smartspawn(...), while checking if variables have been defined:
if(props->aggression != 0)e->aggression = props->aggression; // Aggression can be changed with spawn points now
Again, any comments are appreciated.
This is one I'm pretty happy with, if only because of the fact that it makes using Tail's alpha transparency so much easier. Since it was designed with that in mind, it's naturally based on the code for OpenDarkBoR.
These changes will add two new projectiles, one for players and one for enemies. The player projectile is called 'playshottr' and the enemy version is simply called 'track'. What these projectiles do is automatically follow the position of the entity which shot them. So you could shoot one, then walk across the screen, and the projectile will still be 'attached' to you. This makes it MUCH easier to make characters who have attacks which are partially transparent, especially if the entity moves during the attack.
Once spawned, there are two ways to make the tracker projectile dissapear. The first is to set it not to loop, in which case it will die automatically once it's animation is complete. The other is to let an attack hit the entity which spawned the tracker. That way, the attack won't remain out if something knocks the shooter out of an attack.
These can be used just like any other projectile, except that you
can't set the altitude at which it is fired. Which makes sense, since
it'll always have the same altitude as the entity which fired it.
To define a player's tracker, use the
line
For enemy trackers, use the line
And now, the code:
in the definition for struct 's_anim':int trackframe; // enemy offset-tracking projectile
int pshotframetr; // offset-tracking projectile
char custpshottr[MAX_NAME_LEN+1]; // cust pshot for pshottr
char custtrack[MAX_NAME_LEN+1];
in the definition for struct 's_model':char playshottr[MAX_NAME_LEN+1];
char track[MAX_NAME_LEN+1];
in function 'load_cached_model', while setting defaults:strncpy(newchar->track, "track", MAX_NAME_LEN);
Same function, while reading the file:else if(stricmp(command, "playshottr")==0){
value = findarg(buf+pos, 1);
find_model(value);
strncpy(newchar->playshottr, value, MAX_NAME_LEN);
}
else if(stricmp(command, "track")==0){
value = findarg(buf+pos, 1);
find_model(value);
strncpy(newchar->track, value, MAX_NAME_LEN);
}
Same function, now while setting the defaults for animations:newanim->pshotframetr = -1;
newanim->trackframe = -1;
strncpy(newanim->custtrack, newchar->track, MAX_NAME_LEN);
strncpy(newanim->custpshottr, newchar->playshottr, MAX_NAME_LEN);
Still the same function, while reading in animations:else if(stricmp(command, "trackframe")==0){ // Frame to use tracking shot
newanim->trackframe = atoi(findarg(buf+pos, 1));
newanim->throwa = 0; // No point setting throwa: it'll be ignored!
}
else if(stricmp(command, "pshotframetr")==0){
newanim->pshotframetr = atoi(findarg(buf+pos, 1));
newanim->throwa = 0;
}
else if(stricmp(command, "custpshottr")==0){
value = findarg(buf+pos, 1);
find_model(value);
strncpy(newanim->custpshottr, value, MAX_NAME_LEN);
}
else if(stricmp(command, "custtrack")==0){
value = findarg(buf+pos, 1);
find_model(value);
strncpy(newanim->custtrack, value, MAX_NAME_LEN);
}
Right before the function 'ent_set_anim', to be used later:void pshot_spawntr(float x, float z, float a, int direction, int pi);
void track_spawn(float x, float z, float a, int direction, entity* enem);
Now inside both 'ent_set_anim' and 'ent_reset_anim':// Spawns for new offset-tracking projectiles.
else if(ent->type == TYPE_PLAYER && ent->animpos == ent->animation->pshotframetr){
pshot_spawntr(ent->x, ent->z, ent->a, ent->direction, ent->playerindex);
}
else if(ent->type == TYPE_ENEMY && ent->animpos == ent->animation->trackframe && ent->think){
track_spawn(ent->x, ent->z, ent->a, ent->direction, ent);
}
In the 'update_ents' function, while spawning projectiles:// New tracker projectile
if(self->type == TYPE_PLAYER && self->animpos == self->animation->pshotframetr){
pshot_spawntr(self->x, self->z, self->a, self->direction, self->playerindex);
}
// New Tracker projectile
else if(self->type == TYPE_ENEMY && self->animpos == self->animation->trackframe){
track_spawn(self->x, self->z, self->a, self->direction, self);
}
in function 'player_weapon', while grabbing the info from the original entity:if(newmodel->playshottr) strncpy(player[self->playerindex].model->playshottr, newmodel->playshottr, MAX_NAME_LEN);
else strncpy(player[self->playerindex].model->playshottr, NULL, MAX_NAME_LEN);
Two new functions which go with the other player projectile thinking/spawning functions:// for tracking player projectiles
void range_thinktr(void){
if(freezeall) return; // Don't update animation if special is being executed
if(!self->animating) kill(self); // autokill self when done animation
if(self->owner->think == player_grabbed || self->owner->think == player_pain || self->owner->think == player_fall){
kill(self);
return; // Kill self if owner is attacked
}
if(self->model->speed != 0){
self->model->speed = 0;
}
self->direction = self->owner->direction;
self->x = self->owner->x;
self->a = self->owner->a;
self->z = self->owner->z; // Track the owner!
self->nextthink = time + THINK_SPEED / 2;
}
void pshot_spawntr(float x, float z, float a, int direction, int pi){
entity *e;
e = spawn(x, z, a, player[pi].ent->animation->custpshottr);
if(e==NULL) return;
e->attacking = 1;
e->direction = direction;
e->think = range_thinktr;
e->remove_on_attack = e->model->remove;
e->owner = player[pi].ent; //Need to remember the owner to track them!
e->playerindex = pi;
e->base = a;
e->screen = e->model->alpha;//transparents effect by tails
if(e->model->speed) e->model->speed = 0; // Don't want trackers moving on their own!
}
Two new functions which go with the other enemy projectile thinking/spawning functions:void track_think(void){
if(freezeall) return; // Don't update animation if special is being executed
if(!self->animating) kill(self); // Dissapear when animation is finished
if(self->owner->think == enemy_grabbed || self->owner->think == enemy_takedamage || self->owner->think == biker_takedamage){
kill(self);
return; // Kill self if owner is attacked
}
if(self->model->speed != 0) self->model->speed = 0;
self->direction = self->owner->direction;
self->x = self->owner->x;
self->a = self->owner->a;
self->z = self->owner->z; // Track the owner!
self->nextthink = time + THINK_SPEED / 2;
}
void track_spawn(float x, float z, float a, int direction, entity* enem){
entity *e;
e = spawn(x, z, a, enem->animation->custtrack);
if(e==NULL) return;
e->takedamage = enemy_takedamage;
e->owner = enem; // Need to know the owner to track them!
e->attacking = 1;
if(e->model->speed != 0) e->model->speed = 0;
e->model->subtype = SUBTYPE_EWEAPON; // Flag so projectiles don't fall when players spawn
e->nograb = 1;
e->direction = direction;
e->think = track_think;
e->screen = e->model->alpha; // For Tail's Alpha transparency
e->remove_on_attack = e->model->remove;
e->base = a;
}
Demo screenshots:

.... I don't really have any screenshots at the moment. So I'll reuse this picture..