• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Norman Barrows

adding new feature requires 99 code changes!

40 posts in this topic


I think it's appropriate that this was in comic cans. Is it really possible that one agent could be doing all of that at once? Is it not possible to consolidate certain heartbeat actions (skill reduction, god relations, background radiation, illness damage, etc...) into a single update? Those affected members could all be stuck in a common structure for cache coherency, and affected in a single HeartbeatUpdate call. This, here, seems to me to be the very epitome of the phrase "spaghetti code". It's especially funny that you have a comment saying "Must call move_rafts first" but you actually call BMmodel_raft_movement sixth in line. What does the method run_bandmember() do, if it doesn't handle any of all this? This whole thing just screams for refactoring.

 

<BG>

 

what you see is the result of simply taking the guts of all those update loops and placing each in its own function, then calling them all. many (perhaps 2/3's) are very short 1-3 liners.  further consolidation is almost definitely warranted.   in the mean time i have fastcall and inline any suitable on my side - not that that necessarily means a whole lot.

 

 OTOH, placing the body of the loop in its own function, and then placing the guts of all those routines (at least the smaller ones) in the loop body would have a negative impact on readability. not the loop body as a separate function, that actually makes things clearer - its the adding of all those little 1-3 liners to one big update_each() routine that would detract from the read-abilty of the loop body code.   

 

the general design considerations in software engineering: read-ability, write-ability, maintain-ability, modify-ability, reuse-ability, speed, resource usage, etc. are often at odds with each other.  

 

this is the nature of design considerations in general in all forms of engineering.  

 

thus all design is an exercise in compromise.  

 

good design finds that "sweet spot" trade-off point between competing considerations.

 

personally, i've always been big on read-ability.  i used to give team mates grief in school for using shift instead of add due to lack of readability (it was less English-like, and we didn't need the speed).

1

Share this post


Link to post
Share on other sites

Those affected members could all be stuck in a common structure for cache coherency

 

 

rock shelter, cave, and hut encounter flags are in the list of caves, rock shelters, and huts.

 

everything else is in a caveman struct:

 

 

(gonna give slash code a try here....)

 
 
// cavemen list
struct cavemanrec
{
char name[100];                     
int str,intelligence,dex,con,chr,speed,   // core stats
food,water,sleep,mood,hp,dmg,has_disease,hygiene,     // current stats          
fatigue,   // fatigue * 1000
social,
xp[maxskilltypes],
mx,mz, // map sq             positive x is east. positive z is north.  x=0..4999.   z=0..4999.
active, // 0 or 1. whether the record is in use. 1=active.
current_action,     // what they're doing   0=do nothing.
action_counter,
action_x,
action_z,
moved,            // 1 if they moved this frame and dont recharge fatugue
wpndrawn,           // 1 if wpn drawn
sex,                 // 0=male 1=female
curwpn,             // what kind of weapon they're currently using      (an obj#)    
attacking,        // 0,1
attack_counter,
suprised,
suprise_counter,
using_stealth,
tracking_flag,   // 0=not using tracking. 1=using tracking to avoid encounters. 2=using tracking to find encounters.
running,          // 0 or 1
location,          // a pre-defined location. OUTSIDE, UPATREE, INSHELTER, etc
atk2subdue,        // 0 or 1
curammo,          // obj#   INACTIVE = no ammo selected
actiondata[10],
interests[3],
tgt,                 // used by clanmembers != cm0
rng,                //  rng to tgt
orders,              // used by clanmembers != cm0     0=no orders   1=follow me (ordersdata[0]=who to follow)
ordersdata[10],              // used by clanmembers != cm0     
has_perm_shelter,
shel_mx,
shel_mz,
shel_x,
shel_z,
paddling,
raftnum,
cur_quest,
god[8],
camoflaged,
camotype,
action_qual,
falling,
research_counter[maxskilltypes],
animals_killed,
cavemen_killed,
action_counter2[maxactions],
makeobj_counter[max_object_types],
stackptr,
perm_shel_quality,
aniplayerID,
currentani,
sneak_state,              // 0 not sneaking. 1 sneaking, undetected. 2 sneaking, detected.
collision_recovery,   // 0=not in collision recovery   1= in collision recovery
CR_counter,            // how many turns they've been in collision recovery
entangled,
climb_mode,
crh_index,         // which cave they are in
is_blocking,        // boolean
head_mesh,hair_mesh,skin_tex,face_tex,hair_tex,eye_tex,modelID,
alive;
 
float x,y,z,      // d3d location in the map sq.  pos x is east (0..4999).  pos z is north (0..4999).   pos y is up.
      xr,yr,zr,   // d3d orientation in radians
      vx,vy,vz,
      vxr,vyr,
      CRyr;          // desired heading for collision recovery
objrec2 stuff[maxobjrecs];
//psrec shel[psms][psms];
//psmap shel;
questrec quest[maxquests];
drugrec drug[maxdrugs];
traprec trap[maxtraps];
 
actionstackrec stack[actionstacksize];
 
};
 
 
 
Edited by Norman Barrows
1

Share this post


Link to post
Share on other sites

ok, here's the results of the timing tests.

 

tests were conducted between run_simulation() as posted above, and the new run_simulation2() routine.

 

the new _runsimulation2 routine is identical, except that the many small player update loops have been replaced by one call to BMupdate_all at the beginning of do_global_frame.

 

 

to get meaningful numbers, i was required to measure the time in query performance counter ticks accumulated over of runs of 15 frames.

 

the old way with a bunch of loops clocked in at ~3000 ticks in 15 frames, or ~200 ticks per frame.

 

the new way with a single big loop clocked in at ~1000 ticks in 15 frames, or ~ 66 ticks per frame.

 

not surprising, perhaps a bit more difference than expected.     eliminating loop overhead helped.  not sure how much if any can be attributed to the code being more cache friendly.

 

the generic iterator is next, then consolidating all those one-liners.

 

so is there a zero overhead C++ alternative to a findfirst / findnext generic iterator design?

0

Share this post


Link to post
Share on other sites

Do not post code in blue comic sans on this forum, or anywhere on the Internet, ever again. We have [[size="1"][/size]code] tags, use them.

Edited by rip-off
0

Share this post


Link to post
Share on other sites


It's especially funny that you have a comment saying "Must call move_rafts first" but you actually call BMmodel_raft_movement sixth in line.

 

BMmodel_raft_movement simply moves the player by the same amount as the raft he/she's on.   gotta move the raft first, so you know how much to move the player!   <g>

 

thus there is a call order dependency  between move_rafts and BMmodel_raft_movement, and therefore between move_rafts (update rafts) and BMupdate_all (update PCs) in general.  

 

this is due to the design choice of consolidating the player movement due to rafts into BMupdate_all.      player movement due to rafts  ( BMmodel_raft_movement  )  is actually an "event handler" for a "raft moved" event.  

 

the old way with lots of loops processed the "raft moved" event immediately, which required a loop to iterate over the list of players to move them if they were aboard the raft.  but it was call order independent.   

 

the new way moves the raft and stores the resulting dx,dz movement from the event for later processing by BMupdate_all.  this eliminates the loop in move_rafts, and moves the body of that loop into a call from the loop body of BMupdate_all.  but its not call order independent any longer.    move_rafts must be called first to pickup the event, so BMupdate_all can then process it along with all the other band member updates in one big loop.

 

not sure which i favor.  i tend to like to process events immediately and not leave stuff hanging around.  it can often be simpler.   in this case all it got me was one consolidated loop and a call order dependency i didn't have before.   if call order becomes an issue i can always go back to processing the event immediately.

1

Share this post


Link to post
Share on other sites


Do not post code in blue comic sans on this forum, or anywhere on the Internet, ever again. We have [code] tags, use them.

 

 

i've been having trouble with "[ slash code ]" truncating everything after it in the post, thus the strange font.

 

sorry about that!

1

Share this post


Link to post
Share on other sites
i've hooked up the generic "alive band member" iterator to the main loop in BMupdate_all.
 
i also put the body of the loop in its own function: BMupdate_each(int a);
 
then i collapsed all the calls in BMupdate_each to inline code to create a new version called BMupdate_each2
 
the iterators....
 
 

 
 

int BM_alive_num,BM_active_num;
 
 
int first_alive_bandmember()
{
int a;
for (a=0; a<maxcavemen; a++)
    {
    if (cm[a].active == 0)
        {
        continue;
        }
    if (cm[a].alive == 0)
        {
        continue;
        }
    BM_alive_num=a;
    return(a);
    }
return(-1);
}
 
 
 
 
int next_alive_bandmember()
{
int a;
for (a=BM_alive_num+1; a<maxcavemen; a++)
    {
    if (cm[a].active == 0)
        {
        continue;
        }
    if (cm[a].alive == 0)
        {
        continue;
        }
    BM_alive_num=a;
    return(a);
    }
return(-1);
}
 
 
 
 
 
 
 
 
int first_active_bandmember()
{
int a;
for (a=0; a<maxcavemen; a++)
    {
    if (cm[a].active == 0)
        {
        continue;
        }
    BM_active_num=a;
    return(a);
    }
return(-1);
}
 
 
 
 
 
 
 
 
int next_active_bandmember()
{
int a;
for (a=BM_active_num+1; a<maxcavemen; a++)
    {
    if (cm[a].active == 0)
       {
       continue;
       }
   BM_active_num=a;
   return(a);
   }
return(-1);
}
 
 
 

 
 
here's the new BMupdate_all:
 
 
 
 
//    must call move_rafts first!
void BMupdate_all()
{
int a;
a=first_alive_bandmember();
while (a!=-1)
    {
    BMupdate_each(a);
    a=next_alive_bandmember();
    }
}
 
 
 
 
and now, for a wall of spaghetti code - the new BMupdate_each2 with all the code pulled into one function.
 
 
this thing is probably similar  to that 32,000 line (or whatever it was) update function mentioned previously....
 
it seems to be bigger, uglier, and probably more work to maintain.    one function, 905 line of code.
 
 
 
 
 
 

void BMupdate_each2(int a)
{
int b,c,e,mx,mz,d,num,f,chance,rng;
float h,x,y,z;
char s[100];
//    do update stuff here - do global frame
//   decrease fatigue
if ((cm[a].moved==0) && (!cm[a].paddling) && (action[cm[a].current_action].energy))
    {
    cm[a].fatigue-=7;                               // reduce fatigue.
    if (cm[a].fatigue<0) cm[a].fatigue=0;
    }
//    model attack
if (cm[a].attacking) 
    {
    cm[a].attack_counter++;
    if (on_drug(a,GAHMUHROOT)) c=atktime*3/4; else c=atktime;
    if (cm[a].attack_counter >= c) 
        { 
        cm[a].attacking=0; 
        }
    else if (cm[a].attack_counter==5) resolve_attack(a);
    }
//   model surprise
if (cm[a].suprised)
    {    
    cm[a].suprise_counter++;
    if (cm[a].suprise_counter>60-xpbonus(cm[a].xp[HUNTING])) cm[a].suprised=0;
    }
//  model full fatigue
if (cm[a].fatigue >= 100000)
    {
    if (cm[a].current_action != MOVE) msg("You're so fatigued you have to stop and rest!");
    // dont resume the following actions w/ friendly cavemen, as they may have wandered off while band member was resting
    if ( (cm[a].current_action != TEACHCAVEMAN) && (cm[a].current_action != TELLJOKE) && (cm[a].current_action != TELLSTORY) )  pushaction2(a);   
    b=gamespeed;
    setaction(a,REST);
    gamespeed=b;
    cm[a].action_x=0;     // no bedding
    }
//   run bamdmember
if (a!=cm0) 
    {
    if (  badguys_nearby(a) && (cm[a].current_action != DONOTHING)  )        // halt action if badguys near
        {
        if (cm[a].current_action == MOVE) adjust_bandmember_location(a);
        setaction(a,DONOTHING);
        }
    if (cm[a].current_action==DONOTHING)
        {
        cm[a].is_blocking=0;
        if (cm[a].collision_recovery)
            {
            move_bandmember_collision_recovery(a);
            }
        else
            {
            set_bandmember_tgt(a);
            steer_bandmember(a);
            move_bandmember(a);
            do_bandmember_atk(a);
            }
        }
    else
        {
        b=cm0;
        cm0=a;
        doactionmodeB(a);
        cm0=b;
        }
    }
//     model raft movement
if (cm[a].location == ONRAFT)
    {
    if (cm[a].current_action != MOVERAFT)
        {
b=cm[a].raftnum;
        cm[a].x+=worldobj[b].dx;
        cm[a].z+=worldobj[b].dz;
        normalize_location( &cm[a].mx, &cm[a].mz, &cm[a].x, &cm[a].z );
        cm[a].y=heightmap(cm[a].mx,cm[a].mz,cm[a].x,cm[a].z);
        }
    }
//  model paddling fatigue
if (cm[a].paddling)
    {
    cm[a].fatigue+=1;
    if (cm[a].fatigue>100000) cm[a].fatigue=100000;
    }
//  model falling
if (cm[a].falling) 
    {
    cm[a].vy+=-g2;
    cm[a].x+=cm[a].vx;
    cm[a].y+=cm[a].vy;
    cm[a].z+=cm[a].vz;
    h=heightmap( cm[a].mx, cm[a].mz, cm[a].x,cm[a].z);
    if (cm[a].y <= h ) bandmember_hit_ground(a,h);
    }
//   model sneak detection
if (   (a % 15 == frame) && (cm[a].sneak_state!=0)  )
    {
    if (cm[a].sneak_state==3)
        {
        if (! bandmember_targeted(a)) cm[a].sneak_state=1;
        }
    else       // states 1 & 2
        {
        switch (detection_state(a))
            {
            case 0: cm[a].sneak_state=1;    //   sneaking - undetected
                    break;
            case 1: cm[a].sneak_state=2;     // sneaking - detected - non-hostile
                    break;
            case 2: cm[a].sneak_state=3;     // sneaking - detected - hostile
                    break;
            }
        }
    }
if (frame != 0)
    {
    return;
    }
//  do global second
//  model fatgue due to damage
if  (   
    (! on_drug(a,MUHAHBOOLEAF)) &&
    (! on_drug(a,YAHBEEBARK))   &&
    (! on_drug(a,BAHYUHSEED))   &&
    (! on_drug(a,GAHMUHROOT)) 
    )
    {
    cm[a].fatigue+=10*cm[a].dmg/cm[a].hp;
    if (cm[a].fatigue>=100000) cm[a].fatigue=100000;
    }
//  model fatigue due to encumberance
if (    (!on_drug(a,MUHAHBOOLEAF)) &&   (! on_drug(a,BAHYUHSEED))  )
    {
    cm[a].fatigue+=10*encumberance(a)/maxencumberance(a);
    if (cm[a].fatigue>=100000) cm[a].fatigue=100000;
    }
//  run quests
for (b=0; b<maxquests; b++)
    {
    if (! cm[a].quest[b].active) continue;
    run_quest(a,b);
    }
//  clear rock shelter encounter flags
mx=cm[a].mx;
mz=cm[a].mz;
if ( map[mx][mz].cliffs) 
    {
    for (e=60000; e<65000; e++)
        {
        if (crh2[e].mx != mx) continue;
        if (crh2[e].mz != mz) continue;
        if (crh2[e].encounter_checked)
            {
            if (first_bandmember_near_cave_or_rockshelter(mx,mz,e,300) == -1)
                {
                remove_CRH_cavemen(2,mx,mz,e);     // 2 = rockshelter housetype        removes friendly cavemen only!
                crh2[e].encounter_checked=0;
                if ((crh2[e].owner_type==HOSTILECAVE) && (hostile_CRH_encounter_underway) )
                    {
                    if ((! hostile_fled_CRH_encounter) && (! hostile_in_mapsq(a))) crh2[e].owner_type=EMPTYCAVE;
                    hostile_CRH_encounter_underway=0;
                    hostile_fled_CRH_encounter=0;
                    }
                }
            }
        }
    }
//   check rock shelter encounters
mx=cm[a].mx;
mz=cm[a].mz;
if ( map[mx][mz].cliffs)
    {
    for (e=60000; e<65000; e++)
        {
        if (crh2[e].mx != mx) continue;
        if (crh2[e].mz != mz) continue;
        if (crh2[e].encounter_checked) continue;
        if ( BBdist( (int)cm[a].x, (int)cm[a].z, crh2[e].x,crh2[e].z) > 75) continue;        // trigger at 75, reset at 300
        crh2[e].encounter_checked=1;
        switch ( crh2[e].owner_type )
            {
            case ANIMALCAVE: do_cave_animal_encounter(a); break;
            case FRIENDLYCAVE:  
                                if (crh2[e].npc_ownerID == -1)        // no npc assigned to rockshelter yet, so assign one
                                    {
                                    num=dice(10);
                                    for (b=0; b<num; b++)
                                        {
                                        x=crh2[e].x-20.0f+(float)b*10.0f;
                                        z=crh2[e].z+40.0f;
                                        y=heightmap(mx,mz,x,z);
                                        d=add_caveman(mx,mz,x,y,z,0,0);
                                        if (d != -1)
                                            {
                                            animal[a].butchered=new_npc();
                                            if (b==0) crh2[e].npc_ownerID=animal[d].butchered;
                                            npc[animal[d].butchered].house_type=2;    // house type: 0=unassigned 1=cave 2=rockshelter 3=hut
                                            npc[animal[d].butchered].house_mx=mx;
                                            npc[animal[d].butchered].house_mz=mz;
                                            npc[animal[d].butchered].house_index=e;
                                            animal[d].sacrificed=1;   // 1= they're a CRH caveman
                                            }
                                        }
                                    }
                                else
                                    {
                                    c=0;
                                    for (b=0; b<maxnpcs; b++)
                                        {
                                        if (! npc[b].active) continue;
                                        if (npc[b].house_type != 2) continue;       // if not rockshelter
                                        if (npc[b].house_mx != mx) continue;
                                        if (npc[b].house_mz != mz) continue;
                                        if (npc[b].house_index != e) continue;
                                        x=crh2[e].x-20.0f+(float)c*10.0f;
                                        z=crh2[e].z+40.0f;
                                        y=heightmap(mx,mz,x,z);
                                        d=add_caveman(mx,mz,x,y,z,0,0);
                                        if (d != -1)
                                            {
                                            animal[d].butchered=b;
                                            animal[d].sacrificed=1;   // 1= they're a CRH caveman
                                            }
                                        c++;
                                        }
                                    }
                                msg("You encounter cavemen from the nearby rockshelter!");
                                if (cm[a].current_action == MOVE) adjust_bandmember_location(a);
                                setaction(a,DONOTHING);
                                gamespeed=0;
                                break;
            case HOSTILECAVE:   add_hostile_cavemen(a,0); 
                                msg("You encounter cavemen from the nearby rockshelter!");
                                if (cm[a].current_action == MOVE) adjust_bandmember_location(a);
                                setaction(a,DONOTHING);
                                gamespeed=0;
                                hostile_CRH_encounter_underway=1;
                                hostile_fled_CRH_encounter=0;
                                break;
            case PLAYERCAVE: if (dice(10000)<=1000)  do_cave_animal_encounter(a); break;
            }
        }
    }
//    clear cave encounter flags
mx=cm[a].mx;
mz=cm[a].mz;
if ((map[mx][mz].elevation == 2) || (map[mx][mz].elevation == 3)) 
    {
for (f=0; f<numcaves[mx][mz]; f++)
    {
    e=caveindex[mx][mz][f];
        if (crh2[e].encounter_checked)
            {
            if (first_bandmember_near_cave_or_rockshelter(mx,mz,e,300) == -1)
                {
                remove_CRH_cavemen(1,mx,mz,e);     // 1 = cave housetype        removes friendly cavemen only!
                crh2[e].encounter_checked=0;
                if ((crh2[e].owner_type==HOSTILECAVE) && (hostile_CRH_encounter_underway) )
                    {
                    if ((! hostile_fled_CRH_encounter) && (! hostile_in_mapsq(a))) crh2[e].owner_type=EMPTYCAVE;
                    hostile_CRH_encounter_underway=0;
                    hostile_fled_CRH_encounter=0;
                    }
                }
            }
    }
    for (e=0; e<60000; e++)
        {
        if (crh2[e].mx != mx) continue;
        if (crh2[e].mz != mz) continue;
        if (crh2[e].encounter_checked)
            {
            if (first_bandmember_near_cave_or_rockshelter(mx,mz,e,300) == -1)
                {
                remove_CRH_cavemen(1,mx,mz,e);     // 1 = cave housetype        removes friendly cavemen only!
                crh2[e].encounter_checked=0;
                if ((crh2[e].owner_type==HOSTILECAVE) && (hostile_CRH_encounter_underway) )
                    {
                    if ((! hostile_fled_CRH_encounter) && (! hostile_in_mapsq(a))) crh2[e].owner_type=EMPTYCAVE;
                    hostile_CRH_encounter_underway=0;
                    hostile_fled_CRH_encounter=0;
                    }
                }
            }
        }
    }
//  check cave encounters
mx=cm[a].mx;
mz=cm[a].mz;
if ((map[mx][mz].elevation == 2) || (map[mx][mz].elevation == 3)) 
    {
for (f=0; f<numcaves[mx][mz]; f++)
    {
    e=caveindex[mx][mz][f];
        if (crh2[e].encounter_checked) return;
        if ( BBdist( (int)cm[a].x, (int)cm[a].z, crh2[e].x,crh2[e].z) > 75) continue;        // trigger at 75, reset at 300
        crh2[e].encounter_checked=1;
        switch ( crh2[e].owner_type )
            {
            case ANIMALCAVE: do_cave_animal_encounter(a); break;
            case FRIENDLYCAVE: 
                                if (crh2[e].npc_ownerID == -1)  // no npc assigned to cave yet, so assign one
                                    {
                                    num=dice(10);
                                    for (b=0; b<num; b++)
                                        {
                                        x=crh2[e].x-20.0f+(float)b*10.0f;
                                        z=crh2[e].z+40.0f;
                                        y=heightmap(mx,mz,x,z);
                                        d=add_caveman(mx,mz,x,y,z,0,0);
                                        if (d != -1)
                                            {
                                            animal[d].butchered=new_npc();
                                            if (b==0) crh2[e].npc_ownerID=animal[d].butchered;
                                            npc[animal[d].butchered].house_type=1;    // house type: 0=unassigned 1=cave 2=rockshelter 3=hut
                                            npc[animal[d].butchered].house_mx=mx;
                                            npc[animal[d].butchered].house_mz=mz;
                                            npc[animal[d].butchered].house_index=e;
                                            animal[d].sacrificed=1;   // 1= they're a CRH caveman
                                            }
                                        }
                                    }
                                else
                                    {
                                    c=0;
                                    for (b=0; b<maxnpcs; b++)
                                        {
                                        if (! npc[b].active) continue;
                                        if (npc[b].house_type != 1) continue;    // if not cave
                                        if (npc[b].house_mx != mx) continue;
                                        if (npc[b].house_mz != mz) continue;
                                        if (npc[b].house_index != e) continue;
                                        x=crh2[e].x-20.0f+(float)c*10.0f;
                                        z=crh2[e].z+40.0f;
                                        y=heightmap(mx,mz,x,z);
                                        d=add_caveman(mx,mz,x,y,z,0,0);
                                        if (d != -1)
                                            {
                                            animal[d].butchered=b;
                                            animal[d].sacrificed=1;   // 1= they're a CRH caveman
                                            }
                                        c++;
                                        }
                                    }
                                msg("You encounter cavemen from the nearby cave!");
                                if (cm[a].current_action == MOVE) adjust_bandmember_location(a);
                                setaction(a,DONOTHING);
                                gamespeed=0;
                                break;
            case HOSTILECAVE:   add_hostile_cavemen(a,0); 
                                msg("You encounter cavemen from the nearby cave!");
                                if (cm[a].current_action == MOVE) adjust_bandmember_location(a);
                                setaction(a,DONOTHING);
                                gamespeed=0;
                                hostile_CRH_encounter_underway=1;
                                hostile_fled_CRH_encounter=0;
                                break;
            case PLAYERCAVE: if (dice(10000)<=1000)  do_cave_animal_encounter(a); break;
            }
    }
    }
//    clear hut encounter flags
mx=cm[a].mx;
mz=cm[a].mz;
for (e=65000; e<num_crhs; e++)
    {
    if (! crh2[e].active) continue;
    if (crh2[e].mx != mx) continue;
    if (crh2[e].mz != mz) continue;
    if (crh2[e].encounter_checked)
        {
        if (! bandmember_near_hut(e))            // if no bandmember <= 300 from hut
            {
            remove_CRH_cavemen(3,mx,mz,0);       // 3 = hut housetype        removes friendly cavemen only!
            crh2[e].encounter_checked=0;
            }
        }
    }
//   check hut encounters
mx=cm[a].mx;
mz=cm[a].mz;
for (e=65000; e<num_crhs; e++)
    {
    if (! crh2[e].active) continue;
    if (crh2[e].mx != mx) continue;
    if (crh2[e].mz != mz) continue;
    if (crh2[e].encounter_checked) continue;
    if ( BBdist( (int)cm[a].x, (int)cm[a].z, crh2[e].x,crh2[e].z) > 75) continue;        // trigger at 75, reset at 300
    crh2[e].encounter_checked=1;
    switch ( crh2[e].owner_type )
        {
        case FRIENDLYCAVE: 
                            if (crh2[e].npc_ownerID == -1)        // no npc assigned to hut yet, so assign one
                                {
                                num=dice(10);
                                for (b=0; b<num; b++)
                                    {
                                    x=crh2[e].x-20.0f+(float)b*10.0f;
                                    z=crh2[e].z+30.0f;
                                    y=heightmap(mx,mz,x,z);
                                    d=add_caveman(mx,mz,x,y,z,0,0);
                                    if (d != -1)
                                        {
                                        animal[d].butchered=new_npc();
                                        if (b==0) crh2[e].npc_ownerID=animal[d].butchered;
                                        npc[animal[d].butchered].house_type=3;    // house type: 0=unassigned 1=cave 2=rockshelter 3=hut
                                        npc[animal[d].butchered].house_mx=mx;
                                        npc[animal[d].butchered].house_mz=mz;
                                        animal[d].sacrificed=1;   // 1= they're a CRH caveman
                                        }
                                    }
                                }
                            else
                                {
                                c=0;
                                for (b=0; b<maxnpcs; b++)
                                    {
                                    if (! npc[b].active) continue;
                                    if (npc[b].house_type != 3) continue;        // if not hut
                                    if (npc[b].house_mx != mx) continue;
                                    if (npc[b].house_mz != mz) continue;
                                    x=crh2[e].x-20.0f+(float)c*10.0f;
                                    z=crh2[e].z+30.0f;
                                    y=heightmap(mx,mz,x,z);
                                    d=add_caveman(mx,mz,x,y,z,0,0);
                                    if (d != -1)
                                        {
                                        animal[d].butchered=b;
                                        animal[d].sacrificed=1;   // 1= they're a CRH caveman
                                        }
                                    c++;
                                    }
                                }
                            msg("You encounter cavemen from the nearby hut!");
                            if (cm[a].current_action == MOVE) adjust_bandmember_location(a);
                            setaction(a,DONOTHING);
                            gamespeed=0;
                            break;
        case HOSTILECAVE:   add_hostile_cavemen(a,0); 
                            msg("You encounter cavemen from the nearby hut!");
                            if (cm[a].current_action == MOVE) adjust_bandmember_location(a);
                            setaction(a,DONOTHING);
                            gamespeed=0;
                            break;
        case PLAYERCAVE: if (dice(10000)<=1000)  do_animal_encounter(a); break;
        }
    }
//   check hut takeover
for (b=65000; b<num_crhs; b++)
    {
    if (! crh2[b].active) continue;
    if (! crh2[b].encounter_checked) continue;
    if (crh2[b].mx != cm[a].mx) continue;
    if (crh2[b].mz != cm[a].mz) continue;
    if (BBdist( (int)cm[a].x, (int)cm[a].z,  crh2[b].x, crh2[b].z ) > 50) continue;
    if (cavemen_near_hut(b)) continue;
    msg("The nearby hut has been abandoned !");
    if (cm[a].has_perm_shelter) Znewmenu("Abandon your hut and take this one over ?");
    else Znewmenu("Takeover abandoned hut ?");
    Zaddmenu("Yes");
    Zaddmenu("No");
    if (menu(2)==1)
        {
        // create perm shelter where hut is
        cm[a].has_perm_shelter=1;
        cm[a].shel_mx=cm[a].mx;
        cm[a].shel_mz=cm[a].mz;
        cm[a].shel_x=crh2[b].x;
        cm[a].shel_z=crh2[b].z;
        cm[a].perm_shel_quality=100;
        }
    crh2[b].active=0;
    pcrh2[b].active=0;
    // sort_and_index_huts("Updating huts");
index_huts2();
    cm[a].mood+=10;
    ulim(&cm[a].mood,100);
    }
//   check climbing animals
if (cm[a].location == UPATREE)
    {
    check_animal_climbing_up(a);
    }
if (second != 0)
    {
    return;
    }
//    do global minute
//    check animal encounters
b=animal_encounter_chance();
if (dice(10000) <= b)
    {
    do_animal_encounter(a);
    }
//  check caveman encounters
if (dice(10000) > 10)
    {
    return;
    }
b=caveman_encounter_chance(a);
if (dice(10000) > b)
    {
    return;
    }
do_caveman_encounter(a);
//   model intox
for (b=0; b<maxdrugs; b++)
    {
    if (! cm[a].drug[b].active) continue;
    cm[a].drug[b].age++;
    if (cm[a].drug[b].age>400)
        {
        switch(cm[a].drug[b].type)
            {
            case MUHAHBOOLEAF:          cm[a].dex--;
                                        cm[a].sleep-=50;
                                        llim(&cm[a].sleep,0);
                                        cm[a].fatigue+=50000;
                                        ulim(&cm[a].fatigue,100000);
                                        break;
            case BOOYEEBEEBERRY:        cm[a].intelligence-=2;
                                        break;
            case GAHYEEMOOGUHFLOWER:    cm[a].sleep-=10;
                                        llim(&cm[a].sleep,0);
                                        break;
            case YAHBEEBARK:            cm[a].mood-=40;
                                        llim(&cm[a].mood,0);
                                        break;
            case BAHYUHSEED:            cm[a].str-=3;
                                        cm[a].sleep-=50;
                                        llim(&cm[a].sleep,0);
                                        cm[a].fatigue+=50000;
                                        ulim(&cm[a].fatigue,100000);
                                        break;
            case GAHMUHROOT:            cm[a].speed-=2;
                                        cm[a].sleep-=50;
                                        llim(&cm[a].sleep,0);
                                        cm[a].fatigue+=50000;
                                        ulim(&cm[a].fatigue,100000);
                                        break;
            }
        cm[a].drug[b].active=0;
        }
    }
//  extinguish torches
for (b=0; b<maxobjrecs; b++)
    if (  (cm[a].stuff[b].active) && (cm[a].stuff[b].type==LITTORCH) )
        { 
        cm[a].stuff[b].qual--;
        if (cm[a].stuff[b].qual < 1) cm[a].stuff[b].active=0;
        }
if (minute%7 == 0)
    {
//   reduce hygiene due to movement
    if (cm[a].moved)
        {
        cm[a].hygiene--;
        if (cm[a].running) cm[a].hygiene--;
        if (cm[a].hygiene<0) cm[a].hygiene=0;
        }
    }
if (minute%10 == 0)
    {
//   reduce water
    cm[a].water--; 
    if (cm[a].water<0) cm[a].water=0; 
//  reduce sleep
    cm[a].sleep--; 
    if (cm[a].sleep<=0)
        {
        cm[a].sleep=0;
        if (cm[a].current_action != SLEEP)
            {
            setaction(a,SLEEP);   // sleep
            cm[a].actiondata[0]=0;
            cm[a].actiondata[1]=cm[a].sleep;    // save old sleep value for mood check when they wake up
            cm[a].actiondata[2]=100;
            if ((cm[a].dmg>0) || (cm[a].has_disease))
                {
                if (num_carried(a,MEDHERB,ANYQUALITY) > 0)
                    {
                    remove_best(a,MEDHERB);
                    cm[a].actiondata[3]=1;          // using med herbs
                    }
                else cm[a].actiondata[3]=0;         // not using med herbs
                }
            else cm[a].actiondata[3]=0;           // not using med herbs
            msg("You pass out from lack of sleep!");
            }
        }
    }
if (minute%15 == 0)
    {
//    reduce food
    cm[a].food--; 
    if (cm[a].food<0) cm[a].food=0;
//  affect mood
    if (! on_drug(a,MUHAHBOOLEAF)) 
        {
        if (cm[a].water<1) 
            {
            cm[a].mood--;
            if (cm[a].mood<0) cm[a].mood=0;
            }
        if (cm[a].food<1) 
            {
            cm[a].mood--;
            if (cm[a].mood<0) cm[a].mood=0;
            }
        if (    !on_drug(a,YAHBEEBARK) && !on_drug(a,BAHYUHSEED))
            {
            if (cm[a].dmg>cm[a].hp/2) 
                {
                cm[a].mood--;
                if (cm[a].mood<0) cm[a].mood=0;
                }
            }
        if (cm[a].has_disease) 
            {
            cm[a].mood--;
            if (cm[a].mood<0) cm[a].mood=0;
            }
        if (cm[a].hygiene<1) 
            {
            cm[a].mood--;
            if (cm[a].mood<0) cm[a].mood=0;
            }
        if (encumberance(a)>3*maxencumberance(a)/4) 
            {
            cm[a].mood--;
            if (cm[a].mood<0) cm[a].mood=0;
            }
        if (  (is_raining) &&  (!has_shelter(a))  && !on_drug(a,GAHMUHROOT) && ! on_drug(a,MUHAHBOOLEAF) )      // rain
            {
            cm[a].mood-=2;
            if (cm[a].mood<0) cm[a].mood=0;
            }
        b=cm0;
        cm0=a;
        if (  (windchill(a) < 60.0f) && (!has_shelter(a)) && (! near_fire())   && !on_drug(a,GAHMUHROOT) && !on_drug(cm0,MUHAHBOOLEAF)  )        // cold
            {
            cm[a].mood-=2;
            if (cm[a].mood<0) cm[a].mood=0;
            }
        cm0=b;
        if (temp_at(a) > 80.0f)        // heat
            {
            cm[a].mood--;
            if (cm[a].mood<0) cm[a].mood=0;
            }
        if (cm[a].social<1)            // make social affect mood
            if (dice(10000) <= 23)
                {
                cm[a].mood--;
                if (cm[a].mood<0) cm[a].mood=0;
                }
        }
    }
if (minute != 0)
    {
    return;
    }
//    do global hour
//  reduce hygiene
if (cm[a].hygiene>=1) cm[a].hygiene--;
//   model dehydration
if (cm[a].water<1) 
    {
    if (dice(100)<=12) 
        {
        cm[a].dmg++;
        cm[a].mood-=2;
        llim(&cm[a].mood,0);
        msg("You take damage from dehydration!");
        if (cm[a].dmg>=cm[a].hp)
            {
            msg("You died of dehydration!");
            kill_bandmember(a);
            }
        }
    }
//   model food spoilage
for (b=0; b<maxobjrecs; b++)
    {
    if (!cm[a].stuff[b].active) continue;
    if (object[cm[a].stuff[b].type].spoilchance==0) continue;
    if (dice(10000) >= object[cm[a].stuff[b].type].spoilchance) continue;
    cm[a].stuff[b].qual-=object[cm[a].stuff[b].type].spoilrate;
    if (cm[a].stuff[b].qual<1)
        {
        cm[a].stuff[b].active=0;
        strcpy_s(s,100,"Some of your ");
        strcat_s(s,100,object[cm[a].stuff[b].type].name);
        strcat_s(s,100," spoiled!");
        msg(s);
        }
    }
//  model exposure
if (exposed(a))  
    {
    if (dice(10000) <= 1000) 
        {
        cm[a].dmg++;
        cm[a].mood-=2;
        llim(&cm[a].mood,0);
        msg("You take damage from exposure to cold!");
        if (cm[a].dmg >= cm[a].hp)
            {
            msg("You died from exposure to cold!");
            kill_bandmember(a);
            }
        }
    }
//  model heatstroke
if  (
    (temp_at(a) > 100) &&
    ((cm[a].location==OUTSIDE) && (map[cm[a].mx][cm[a].mz].coverage!=WOODS))   &&
    ((cm[a].location==OUTSIDE) && (map[cm[a].mx][cm[a].mz].coverage!=JUNGLE))  &&
    (! has_shelter(a)) &&
    (dice(10000) <= 1000)
    )
    {
    cm[a].dmg++;
    cm[a].mood-=2;
    llim(&cm[a].mood,0);
    msg("You take damage from heatstroke!");
    if (cm[a].dmg >= cm[a].hp)
        {
        msg("You died from heatstroke!");
        kill_bandmember(a);
        }
    }
//   model drown in flood
if (watertable>6.0f)                                                                // model drowning in floods
    if(watertable>heightmap(cm[a].mx,cm[a].mz,cm[a].x,cm[a].z)+7.0f)   
        { 
        msg("You have been swept away by flood waters and drown!"); 
        kill_bandmember(a); 
        }
//   model wear n tear of clothing etc
for (b=0; b<maxobjrecs; b++)
    {
    if (!cm[a].stuff[b].active) continue;
    switch(cm[a].stuff[b].type)
        {
        case BOOTS:
        case HAT:
        case MITTENS:
        case CLOAK:
                    if ( is_raining && ! has_shelter(a) ) chance = 1000;
                    else chance = 462;
                    if (dice(10000) > chance) break;
                    cm[a].stuff[b].qual--;
                    if (cm[a].stuff[b].qual>0) break;
                    Ss("Your ");
                    Sa(object[cm[a].stuff[b].type].name);
                    Sa(" wore out!");
                    msg(S);
                    cm[a].stuff[b].active=0;
                    break;
        }
    }
//   model perm shelter raids
if (cm[a].has_perm_shelter)
    {
    if (dice(10000) <= 100)
        {
        chance = 200 - xpbonus(cm[a].xp[CAMOFLAGE]);
        rng=rng2closest_hostile_CRH(cm[a].shel_mx,cm[a].shel_mz);
        if (rng<1) rng=1;
        chance+=200/rng;
        if (dice(10000) <= chance) raid_perm_shelter(a);
        }
    }
//    moodboost nature lovers
if  (   
    (dice(10000) <= 2500)  &&   
    (has_interest(a,I_NATURE))   &&
    (   (map[cm[a].mx][cm[a].mz].coverage == WOODS) ||  (map[cm[a].mx][cm[a].mz].coverage == JUNGLE) || (map[cm[a].mx][cm[a].mz].rocks)  )
    )
    {
    cm[a].mood++; 
    ulim(&cm[a].mood,100);
    }
if (hour%2 == 0)
    {
//  model damage due to illness
    if (   (cm[a].has_disease)  &&   (dice(1000) < 100-xpbonus(cm[a].xp[HEALING]))   )
        {
        cm[a].dmg++;
        cm[a].mood-=2;
        llim(&cm[a].mood,0);
        msg("You take damage from being sick!");
        if (cm[a].dmg>=cm[a].hp)
            {
            msg("You died from an illness!");
            kill_bandmember(a);
            }
        }
    }
if (((hour>7)&&(hour<19)))
    {
//  check quest encounters
    if (  (at_friendly_CRH(a)) &&  (num_friendlies() > 0) &&  (dice(10000) <= 1000)  )   do_quest_encounter(a);
    }
if (hour != 0)
    {
    return;
    }
//    do global day
//  model starvation
if (   (cm[a].food<1)  &&  (dice(100)<=42)  )
    {
    cm[a].dmg++;
    msg("You take damage from starvation!");
    cm[a].mood-=5;
    llim(&cm[a].mood,0);
    if (cm[a].dmg>=cm[a].hp)
        {
        msg("You died of starvation!");
        kill_bandmember(a);
        }
    }
//  model getting sick
if (cm[a].hygiene<1) b=100; else b=20;    // low hygiene increases chance
b-=cm[a].con;                              // constitution decreases chance
if (exposed(a) || exposed2(a)) b+=300;   // exposure increases chance
if (dice(1000)<b)
    {
    cm[a].has_disease=1;
    msg("You've caught an illness!");
    cm[a].mood-=10;
    llim(&cm[a].mood,0);
    }
//  model background radiation - IE ageing
if (dice(10000) <= 3) 
    {
    msg("You suffer aging from background radiation!");
    cm[a].hp--;
    cm[a].mood-=10;
    llim(&cm[a].mood,0);
    if ( (cm[a].hp<1) || (cm[a].dmg>=cm[a].hp) )
        {
        msg("Background radiation (old age) killed you!");
        kill_bandmember(a);
        }
    }
//   model permanent shelter weathering
if ( cm[a].has_perm_shelter)
    {
    if (dice(10000) <= 6000)
        {
        cm[a].perm_shel_quality--;
        if (cm[a].perm_shel_quality < 1) 
            {
            cm[a].has_perm_shelter=0;
            msg("Your shelter has weathered away!");
            }
        }
    }
//  zero god relations
for (b=0; b<8; b++)
    {
    if (cm[a].god[b] > 0) cm[a].god[b]--;
    if (cm[a].god[b] < 0) cm[a].god[b]++;
    }
//   reduce social
cm[a].social-=5; 
llim(&cm[a].social,0);         // make social go down by 5 per day
//   model traps
for (b=0; b<maxtraps; b++)
    {
    if (!cm[a].trap[b].active) continue;
    cm[a].trap[b].age++;
    if (cm[a].trap[b].age>30)
        {
        cm[a].trap[b].active=0;
        continue;
        }
    switch(cm[a].trap[b].status)
        {
        case 0: c=dice(10000);
                if (c<=500) cm[a].trap[b].status=1; // caught animal
                else if (c<=1000) cm[a].trap[b].status=2;   // trap sprung, no animal caught
                break;
        case 1: cm[a].trap[b].daystrapped++;
                if (cm[a].trap[b].daystrapped > 5) cm[a].trap[b].status=2;   // trap sprung, no animal caught (rotted away)
                break;
        }
    }
//  model skill reduction
for (b=0; b<maxskilltypes; b++)
    {
    if (cm[a].xp[b] <1 ) continue;
    c=cm[a].xp[b];
    c/=100;
    if (c<1) continue;
    if (dice(10000) > 274) continue;  // works out to -1%, 10x / year on avg.
    cm[a].xp[b]-=c;
    }
}
 
 
 
 
 
 
 
compare that to BMupdate_each:
 
 
 
 
 

void BMupdate_each(int a)
{
//    do update stuff here - do global frame
BMdecrease_fatigue(a);
BMmodel_attack(a);
BMmodel_surprise(a);
BMmodel_full_fatigue(a);
run_bandmember(a);
BMmodel_raft_movement(a);
BMmodel_paddling_fatigue(a);
BMmodel_falling(a);
BMmodel_sneak_detection(a);
if (frame != 0)
    {
    return;
    }
//  do global second
BMmodel_fatigue_dueto_damage(a);
BMmodel_fatigue_dueto_encumbrance(a);
BMrun_quests(a);
BMclear_rockshelter_enccouter_flags(a);
BMcheck_rockshelter_encounters(a);
BMclear_cave_encounter_flags(a);
BMcheck_cave_encounters(a);
BMclear_hut_encouter_flags(a);
BMcheck_hut_encounters(a);
BMcheck_hut_takeover(a);
BMcheck_climbing_animals(a);
if (second != 0)
    {
    return;
    }
//    do global minute
BMcheck_animal_encounters(a);
BMcheck_caveman_encounters(a);
BMmodel_intox(a);
BMextinguish_torches(a);
if (minute%7 == 0)
    {
    BMreduce_hygiene_dueto_movement(a);
    }
if (minute%10 == 0)
    {
    BMreduce_water(a);
    BMreduce_sleep(a);
    }
if (minute%15 == 0)
    {
    BMreduce_food(a);
    BMaffect_mood(a);
    }
if (minute != 0)
    {
    return;
    }
//    do global hour
BMreduce_hygiene(a);
BMmodel_dehydration(a);
BMmodel_food_spoilage(a);
BMmodel_exposure(a);
BMmodel_heatstroke(a);
BMmodel_drown_in_flood(a);
BMmodel_wearNtear(a);
BMmodel_perm_shelter_raids(a);
BMmoodboost_nature_lover(a);
if (hour%2 == 0)
    {
    BMmodel_damage_dueto_illness(a);
    }
if (((hour>7)&&(hour<19)))
    {
    BMcheck_quest_encounters(a);
    }
if (hour != 0)
    {
    return;
    }
//    do global day
BMmodel_starvation(a);
BMmodel_getting_sick(a);
BMmodel_background_radiation(a);
BMmodel_perm_shel_weathering(a);
BMzero_god_relations(a);
BMreduce_social(a);
BMmodel_traps(a);
BMmodel_skill_reduction(a);
}
 
 
 
 
 
 
 
BMupdate_each seems much clearer to me.
 
 
Although i'm technically not optimizing here, while i have everything setup already i might as well put  timers on the two update_each functions just for fun. Edited by Norman Barrows
0

Share this post


Link to post
Share on other sites

BMupdate_each seems much clearer to me.

 
Although i'm technically not optimizing here, while i have everything setup already i might as well put  timers on the two update_each functions just for fun.

 

 

Once ago I was writing some tile roguelike rpgs (eye1, eye2, eye3 - three big versions of this, not finished but improving ) and I can say my code was very like like yours, also a very big nice structures and a very big complicated stats and skills mechanics - also I discoveed the think you wrote that i 

run many such mechanics in separate way like yours - 

I consider the style of yr coding as a very fine (same way I write as I say) so I read it with nice, Its quite kool - Got not to much mood to get deeper into that you write (there is to much post on this forum and its harder to stop on something separate) but maybe i should becouse we are doing same things!

-2

Share this post


Link to post
Share on other sites


I'm... staggered... you go on and on about 'cache efficiency' and then post a struct like that.. that abomination... which is just going to utterly utterly screw the cache over as it's just so damned large...

 

 

R.O.T.F.L.!!!!!!!     no sh*t!      <g>

 

that data structure is totally    UN-optimized !  <g>

 

too big,   poorly laid out.      screams for a C-E system.

 

like you say, its way too big for the cache, data accesses jump all the heck all over. 

 

that will most likely be addressed during the "quest for 900x accelerate time" optimizations.

 

right now, this "99 code changes" issue is more of a refactoring or re-design to simplify the code.  but while refactoring the code, i do need to keep speed in mind as well as maintainability.  thus the interest in cache friendly code (as opposed to cache friendly data).

 

despite its cache unfriendliness, its still plenty fast enough right now to run update at 450 Hz (fps) - fast enough for any normal shooter.  but not fast enough for a  vehicle sim that supports accelerated times on the order of 1024x - unless its one of those "go get a cup of coffee while you travel in accelerated time to your target" type vehicle sims.

1

Share this post


Link to post
Share on other sites

i ran timing tests on BMupdate_each  (function calls)  vs BMupdate_each2   (905 lines of code)  

 

to get meaningful numbers i had to accumulate the elapsed time for BMupdate_all over 1 second. 

 

times were about a wash, with the nod going to BMupdate_each (function calls) with perhaps 5% fewer ticks on average.

 

this actually makes sense.   its doubtful that a 905 line function compiles to a pile of machine code small enough to be instruction cache friendly.

 

the function version most likely  inlines the short stuff and does calls for the larger update routines.

 

if this were an academic exercise, i'd turn warning level 5 on, and do a build, and check the god-awful long output list of inlined functions to find out.

 

so i think i'm gong to go with the function version, more maintainable, and perhaps a tad faster too.

 

now i have to figure out what to do about all the loops in the checks

0

Share this post


Link to post
Share on other sites

After collapsing all the update loops together, i'm still left with approximately 50 check loops. 

 

I checked for possible C++ iterator constructs, but all were template based with RTT check code overhead, and therefore a slight performance hit. since the code only runs at ~450Hz currently, and the ideal target is an undoable ~13.5KHz, similar constructs without the RTToverhead would be preferable.  

 

to that end, i took a cue from ApochPiQ, and created a generic foreach_aliveBM()  iterator routine which takes a function as a parameter.  

 

that turns code like this....

 

 
 
// reduce fatigue due to movement
int a
for (a=0; a<maxcavemen; a++)
     {
     if (! cm[a].active) continue;
     if (! cm[a].alive) continue;
     if (cm[a].moved)
          {
          cm[a].fatigue--;
          if (cm[a].speed==SPRINT) cm[a].fatigue-=2; else cm[a].fatigue--;
          }
    }
 

 

 

 

into something like this......

 

 

 
 
 
 
// reduce fatigue due to movement
void update_fatigue(int a)
{
if (cm[a].moved) 
    {
    cm[a].fatigue--;
    if (cm[a].speed==SPRINT) cm[a].fatigue-=2; else cm[a].fatigue--;
    }
}
 
 
 
foreach_aliveBM(update_fatigue);
 
 
0

Share this post


Link to post
Share on other sites

here's the foreach generic iterator code....

 
typedef void functiontype2(int);
 
 

void foreach_aliveBM(functiontype2 *f)
{
int a;
for (a=0; a<num_bandmembers-1; a++)   if (cm[a].alive) f(a);
}
 
 
void foreach_activeBM(functiontype2 *f)
{
int a;
for (a=0; a<num_bandmembers-1; a++)  f(a);
}
 
 
Edited by Norman Barrows
0

Share this post


Link to post
Share on other sites

post got clipped cliped again!

 

 

continuing form last post.....

 

 

and here's the new version of BMupdate_all that uses the foreach routine....

 

(still have to call move_rafts first... <g>  )

 
 
//    must call move_rafts first!
void BMupdate_all()
{
foreach_aliveBM(BMupdate_each);
}
 
 
Edited by Norman Barrows
1

Share this post


Link to post
Share on other sites

I'm just curious what the point of all this posting is. Are you asking for help with your (godawful spaghetti) code, or what? The running commentary here just seems sort of bizarre, and probably more suited for a journal or blog of some sort than the Game Programming forum.

0

Share this post


Link to post
Share on other sites

I'm just curious what the point of all this posting is. Are you asking for help with your (godawful spaghetti) code, or what? The running commentary here just seems sort of bizarre, and probably more suited for a journal or blog of some sort than the Game Programming forum.

 

 

i've often found that when looking at threads to learn things, the threads usually don't tell you how things finally tuned out, which is often where the real lessons to be learned are.

 

so those last couple of posting were postmortem / "for posterity" so to speak.

 

i was about to close the thread with a final posting of how the fixing of the 50 check loops went. and the fact that many of the loop bodies had different parameters, which would have required many function prototypes. and how i therefore ended up using the first_alive / next_alive routines for all of them. also, after all that, when changing kill_bandmember, i realized that i couldn't use the "swap and decrement count" trick, as some game objects have an "owner"  which is simply the index of the PC struct. swapping would mean fixing up owner references.  both considerations that anyone else dealing with this type of thing might be interested in.

 

and that's about it. i'm about to finish modding kill_bandmember, then compile and go look at my new dead bandmembers in the game! <g>.

 

still have to make it deactivate them at some point. such as when the remaining players move far away, or after some period of time, etc.  

 

i'd like to thank all for the ideas and suggestions.

 

until next time,

 

hailing frequencies closed

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0