Generated from battle.c with ROBODoc v3.2.2 on Sun Aug 27 19:47:33 2000

TABLE OF CONTENTS

  1. GalaxyNG/addShot
  2. GalaxyNG/allocEnemies
  3. GalaxyNG/allocProtocol
  4. GalaxyNG/attack
  5. GalaxyNG/attackersLeft
  6. GalaxyNG/canShoot
  7. GalaxyNG/doBattle
  8. GalaxyNG/fightphase
  9. GalaxyNG/isBattle
  10. GalaxyNG/isDraw
  11. GalaxyNG/isWon
  12. GalaxyNG/mustBattle
  13. GalaxyNG/resetSides
  14. GalaxyNG/selectAttackingGroup
  15. GalaxyNG/selectTargetGroup
  16. GalaxyNG/shoot

GalaxyNG/addShot

NAME
  addShot --

GalaxyNG/allocEnemies

NAME
   allocEnemies --
FUNCTION


GalaxyNG/allocProtocol

NAME
   allocProtocol -- allocate a battle protocol structure
FUNCTION


GalaxyNG/attack

NAME
   attack -- let one ship fire on another ship
FUNCTION
   Let one ship fire on another ship and if the ship got destroyed update
   the battle statistics.
INPUTS
   aProtocol      -- battle protocol structure to store info about
                     who fired on who
   attackingSide  -- batstat of the side that own the attacking ship
   attackingGroup -- group the attacking ship belongs to
   attackingShip  -- number of the ship within the group
   targetSide     -- batstat of the side that own the target ship
   targetGroup    -- group the target ship belongs to
   targetShip     -- number of the ship within the group
SEE ALSO
   shoot()
SOURCE
    void
    attack(bprotocol *aProtocol,
           batstat *attackingSide, group *attackingGroup, int attackingShip,
           batstat *targetSide, group *targetGroup, int targetShip)
    {
    
      if (shoot(attackingGroup, targetGroup)) {
        targetGroup->left--;
        if (targetGroup->type->weapons) {
          if ((targetGroup->canshoot)[targetShip]) {
            (targetGroup->canshoot)[targetShip] = FALSE;
            targetGroup->numberOfAttackersLeft--;
            targetSide->numberOfAttackersLeft--;
          }
        }
        addShot(aProtocol, attackingSide->who, attackingGroup->type,
                targetSide->who, targetGroup->type, 1);
        targetSide->numberOfTargets--;
        targetSide->who->masslost += typemass(targetGroup->type);
      }
      else {
        addShot(aProtocol, attackingSide->who, attackingGroup->type,
                targetSide->who, targetGroup->type, 0);
      }
    }    

GalaxyNG/attackersLeft

NAME
   attackersLeft -- are there any ships that haven't fired yet?
FUNCTION
   Scan's all batstats to find ships that haven't fired yet.
   If one is found return TRUE. 
INPUTS
   aBatstat -- pointer to the first batstat in list of batstats.
RESULT
   TRUE  -- There are ships left that can fire
   FALSE -- All ships have fired their guns.
SOURCE
    int
    attackersLeft(batstat *aBatstat)
    {
      for (; aBatstat; aBatstat = aBatstat->next) {
        if (aBatstat->numberOfAttackersLeft) {
          return TRUE;
        }
      }
      return FALSE;
    }    

GalaxyNG/canShoot

NAME
   canShoot -- are there armed groups in a list of groups
FUNCTION
SYNOPSIS
   int canShoot(group *g)
INPUTS
   g -- pointer to the first group in the list of groups of a player.
RESULT
   TRUE -- player has ships with a gun.
SOURCE
    int
    canShoot(group *g)
    {
      int             can;
    
      for (can = FALSE; g; g = g->next) {
        if (g->type->attacks) {
          can = TRUE;
          break;
        };
      }
      return can;
    }    

GalaxyNG/doBattle

NAME
   doBattle --
FUNCTION
  Repeat the following until there is a draw or a win:
   (1) Repeat until all ships have fired.
   (1.1) Select randomly a ship from all ships that have not fired yet 
   (1.2) For each gun of the ship
       (1.2a) Select from the enemies of the owner of the ship, randomly
              a ship.
       (1.2b) attack this ship.
  
SOURCE
    void
    doBattle(batstat *batstats, bprotocol *aProtocol, int phase)
    {
      int             attackingShip;
      int             targetShip;
      group          *attackingGroup;
      group          *targetGroup;
      batstat        *attackingSide;
      batstat        *targetSide;
      int             gun;
    
      pdebug(DFULL, "doBattle\n");
    
      resetSides(batstats);
      while (!isDraw(batstats) && !isWon(batstats)) {
        while (attackersLeft(batstats)) {
          attackingGroup =
              selectAttackingGroup(batstats, &attackingSide, &attackingShip);
          assert(attackingGroup);
          attackingGroup->flags |= phase;
    
          for (gun = 0; gun < attackingGroup->type->attacks; gun++) {
            targetGroup =
                selectTargetGroup(attackingSide, &targetSide, &targetShip);
            if (targetGroup) {
              targetGroup->flags |= phase;
              attack(aProtocol,
                     attackingSide, attackingGroup, attackingShip,
                     targetSide, targetGroup, targetShip);
            }
          }
        }
        resetSides(batstats);
      }
    }    

GalaxyNG/fightphase

NAME
   fightphase
SYNOPSIS
   void fightphase(game *aGame, int phase) 
FUNCTION
   Itterates over all planets and checks where any battles
   need to take place. Allocates and initializes the necessary
   battle structures and calls the function to carry out the battle.

   The code works on three basic structures:
     battle       keeps track of each battle.
     participant  keeps track of the participants in each battle, contains
                  the groups that take part in the battle.
     batstat      keeps track of the battle statistics during a battle.
     group        keeps track of which ship in the group fired, which
                  have been shot, and how many there are left that can fire.
INPUTS
   aGame --
   phase -- there are two fight phases, this tells which phase we are
            in. Is used for the battle protocol option.
SOURCE
    void
    fightphase(game *aGame, int phase)
    {
      planet         *p;
    
      pdebug(DFULL, "fightphase\n");
      plog(LFULL, "Checking for battles\n");
      for (p = aGame->planets; p; p = p->next) {
        battle         *b;
    
        if ((b = isBattle(aGame->players, p))) {
          batstat        *batstats;
          batstat        *aBatstat;
          participant    *part;
    
          plog(LFULL, "Battle at %s\n", p->name);
          pdebug(DFULL2, "Battle at %s\n", p->name);
          b->phase = phase;
          batstats = NULL;
          for (part = b->participants; part; part = part->next) {
            aBatstat = allocStruct(batstat);
    
            aBatstat->who = part->who;
            aBatstat->groups = part->groups;
            addList(&batstats, aBatstat);
          }
    
          allocEnemies(batstats);
          doBattle(batstats, b->protocol, phase);
          addList(&(aGame->battles), b);
          updateGroups(b);
    
          for (aBatstat = batstats; aBatstat; aBatstat = aBatstat->next) {
            free(aBatstat->enemies);
          }
          freelist(batstats);
        }
      }
    }    

GalaxyNG/isBattle

NAME
   isBattle -- should a battle take place on a planet?
SYNOPSIS
   battle  *isBattle(player *players, planet *p)
   aBattle = isBattle(players, aPlanet)
FUNCTION
   Checks if a battle should take place on a planet based
   on the groups that orbit a planet.
RESULT
   aBattle  -- initialized battle structure.
               NULL, no battle needs to take place.
 NOTE 
   This function does much more than just determine of a battle should
   take place.  It also initializes and allocates various data structures
   that are needed for the battle simulation. (Should be split in parts)
SOURCE
    battle         *
    isBattle(player *players, planet *p)
    {
      player         *side;
      participant    *participants;
      battle         *aBattle;
    
      pdebug(DFULL, "isBattle\n");
    
      participants = NULL;
      aBattle = NULL;
    
      for (side = players; side; side = side->next) {
        group          *aGroup;
    
        for (aGroup = side->groups; aGroup; aGroup = aGroup->next) {
          if (aGroup->location == p) {
            participant    *aParticipant;
            aParticipant = allocStruct(participant);
    
            assert(aParticipant != NULL);
            aParticipant->who = side;
            aParticipant->groups = NULL;
            addList(&participants, aParticipant);
            break;
          }
        }
      }
    
      if (numberOfElements(participants) >= 2) {
        participant    *aParticipant;
    
        for (aParticipant = participants;
             aParticipant;
             aParticipant = aParticipant->next) {
          group          *aGroup;
    
          for (aGroup = aParticipant->who->groups;
               aGroup;
               aGroup = aGroup->next) {
            if (aGroup->location == p) {
              group          *newGroup;
              newGroup = allocStruct(group);
    
              assert(newGroup != NULL);
              *newGroup = *aGroup;
              assert(newGroup->ships == aGroup->ships);
              newGroup->left = aGroup->ships;
              addList(&(aParticipant->groups), newGroup);
            }
          }
        }
    
        if (mustBattle(participants)) {
          participant    *aParticipant;
    
          aBattle = allocStruct(battle);
    
          assert(aBattle != NULL);
          aBattle->participants = participants;
          aBattle->where = p;
          aBattle->protocol = allocProtocol();
          for (aParticipant = participants;
               aParticipant;
               aParticipant = aParticipant->next) {
            group          *aGroup;
    
            for (aGroup = aParticipant->groups;
                 aGroup;
                 aGroup = aGroup->next) {
              aGroup->canshoot = alloc(sizeof(int) * (aGroup->ships));
    
              assert(aGroup->canshoot);
            }
          }
        }
      }
      if (aBattle == NULL) {
        participant    *r, *r2;
    
    /* this should be a function, it is repeated in freebattle() */
        pdebug(DFULL2, "isBattle : freeing  participants.\n");
        r = participants;
        while (r) {
          freelist(r->groups);
          r2 = r->next;
          free(r);
          r = r2;
        }
      }
      return aBattle;
    }    

GalaxyNG/isDraw

NAME
   isDraw -- is a battle a draw?
SYNOPSIS
   int isDraw(batstat *batstats)
FUNCTION
   Determines whether a battle is a draw or not.  This is the case
   for each of the participants their biggest gun is not strong
   enough the destroy the enemy ship with the smallest shield.
INPUTS
   batstats -- first batstat in the list of batstats.
RESULT
   TRUE  -- the battle is a draw
   FALSE -- otherwise
SOURCE
    int
    isDraw(batstat *batstats)
    {
      int             draw;
      batstat        *aBatstat;
    
      pdebug(DFULL, "isDraw\n");
    
    
      for (aBatstat = batstats;
           aBatstat;
           aBatstat = aBatstat->next) {
        double          maxAttack, curAttack, minDefense, curDefense;
        group          *aGroup;
    
        maxAttack = 0.0;
        minDefense = -1.0;
        for (aGroup = aBatstat->groups;
             aGroup;
             aGroup = aGroup->next) {
          if (aGroup->left) {
            curAttack = aGroup->attack;
            if (curAttack > maxAttack)
              maxAttack = curAttack;
            curDefense = aGroup->defense;
            if (minDefense < 0)
              minDefense = curDefense;
            else if (curDefense < minDefense)
              minDefense = curDefense;
          }
        }
        aBatstat->maxAttack = maxAttack;
        aBatstat->minDefense = minDefense;
      }
    
      draw = TRUE;
      for (aBatstat = batstats;
           aBatstat;
           aBatstat = aBatstat->next) {
        int             enemy;
    
        for (enemy = 0;
             enemy < aBatstat->noEnemies;
             enemy++) {
          batstat        *targetSide;
    
          targetSide = aBatstat->enemies[enemy];
          if (targetSide->minDefense >= 0) {
            if (targetSide->minDefense / 4.0 < aBatstat->maxAttack) {
              draw = FALSE;
            }
          }
        }
      }
      return draw;
    }    

GalaxyNG/isWon

NAME
   isWon -- is a battle is won?
FUNCTION
   Determine if the battle is won by any of the sides.
   A battle is won if the battle is not a draw and none of
   the sides in the battle has any targets left.
SOURCE
    int
    isWon(batstat *aBatstat)
    {
      int             iswon = TRUE;
    
      for (;
           aBatstat && iswon;
           aBatstat = aBatstat->next) {
        int             enemy;
    
        if (aBatstat->numberOfAttackers) {
          for (enemy = 0;
               enemy < aBatstat->noEnemies;
               enemy++) {
            if ((aBatstat->enemies)[enemy]->numberOfTargets) {
              iswon = FALSE;
              break;
            }
          }
        }
      }
    
      return iswon;
    }    

GalaxyNG/mustBattle

NAME
   mustBattle -- check whether participants are gonnna battle
SYNOPSIS
   int mustBattle(participant *participants)
FUNCTION
   Checks if the participants in a battle are actually at war
   with each other and have the necessary means to wage war.
SEE ALSO
   isBattle()
SOURCE
    int
    mustBattle(participant *participants)
    {
      int             war;
      participant    *part1, *part2;
    
      pdebug(DFULL, "mustBattle\n");
      war = FALSE;
      for (part1 = participants; part1 && !war; part1 = part1->next) {
        for (part2 = participants; part2; part2 = part2->next) {
          if (part1 != part2) {
            if (atwar(part1->who, part2->who) && canShoot(part1->groups)) {
              war = TRUE;
              break;
            }
          }
        }
      }
      return war;
    }    

GalaxyNG/resetSides

NAME
   resetSides -- reset all groups for another round of fighting
FUNCTION
   Reset the counters in all batstats so that all ships can fire their
   guns ones more.  Also computes the number of targets.
INPUTS
   batstats -- first batstat in the list of batstats.
SOURCE
    void
    resetSides(batstat *aBatstat)
    {
      for (; aBatstat; aBatstat = aBatstat->next) {
        group          *aGroup;
    
        aBatstat->numberOfAttackers = 0;
        aBatstat->numberOfTargets = 0;
        for (aGroup = aBatstat->groups;
             aGroup;
             aGroup = aGroup->next) {
          int             ship;
    
          if (aGroup->type->attacks) {
            for (ship = 0;
                 ship < aGroup->left;
                 ship++) {
              (aGroup->canshoot)[ship] = TRUE;
            }
            aGroup->numberOfAttackers = aGroup->left;
            aGroup->numberOfAttackersLeft = aGroup->left;
            aBatstat->numberOfAttackers += aGroup->left;
          }
          else {
            aGroup->numberOfAttackers = 0;
            aGroup->numberOfAttackersLeft = 0;
          }
          aBatstat->numberOfTargets += aGroup->left;
        }
        aBatstat->numberOfAttackersLeft = aBatstat->numberOfAttackers;
      }
    }    
    
    

GalaxyNG/selectAttackingGroup

NAME
   selectAttackingGroup -- select a group and a ship in this group
FUNCTION
   Randomly select the next ship that is going to attack.
   This ship is selected from all the ships that have not fired yet.
SOURCE
    group          *
    selectAttackingGroup(batstat *batstats, batstat **attackingSide, int *ship)
    {
      int             totalNumberOfAttackersLeft;
      batstat        *aBatstat;
    
    // Compute the number of ships that haven't fired yet.
      totalNumberOfAttackersLeft = 0;
      for (aBatstat = batstats;
           aBatstat;
           aBatstat = aBatstat->next) {
        totalNumberOfAttackersLeft += aBatstat->numberOfAttackersLeft;
      }
    
      if (totalNumberOfAttackersLeft > 0) {
        int             lowerLim;
        int             upperLim;
        int             n;
    
        lowerLim = 0;
    /* Randomly select one of these ships */
        n = frand3(totalNumberOfAttackersLeft);
    
    /* Find the the owner and the group the ship belongs to. */
        for (aBatstat = batstats;
             aBatstat;
             aBatstat = aBatstat->next) {
          upperLim = lowerLim + aBatstat->numberOfAttackersLeft;
          if ((n >= lowerLim) &&
              (n < upperLim)) {
            group          *aGroup;
    
    /* Found the owner */
            *attackingSide = aBatstat;
            n -= lowerLim;
            lowerLim = 0;
    /* Now find the group the ship belongs to */
            for (aGroup = (*attackingSide)->groups;
                 aGroup;
                 aGroup = aGroup->next) {
              upperLim = lowerLim + aGroup->numberOfAttackersLeft;
              if ((n >= lowerLim) &&
                  (n < upperLim)) {
                int             curShip;
    
                n -= lowerLim;
    /* Now find the number of the ship within the group */
                for (*ship = 0, curShip = 0;
                     *ship < aGroup->numberOfAttackers;
                     (*ship)++) {
    /* Did the ship fire yet? */
                  if ((aGroup->canshoot)[*ship]) {
                    if (n == curShip) {
                      aGroup->numberOfAttackersLeft--;
                      (*attackingSide)->numberOfAttackersLeft--;
                      (aGroup->canshoot)[*ship] = FALSE;
                      return aGroup;
                    }
                    else {
                      curShip++;
                    }
                  }
                }
              }
              lowerLim = upperLim;
            }
          }
          lowerLim = upperLim;
        }
      }
      return NULL;
    }    

GalaxyNG/selectTargetGroup

NAME
   selectTargetGroup -- randomly select a group
SYNOPSIS
FUNCTION
INPUTS
   attackingSide -- batstat of the player that is about to fire  
RESULT
   targetSide -- the owner of the selected group 
   aGroup     -- the selected group
   ship       -- the selected ship in the group
SOURCE
    group          *
    selectTargetGroup(batstat *attackingSide, batstat **targetSide, int *ship)
    {
      group          *targetGroup;
      int             enemy;
      int             totalNumberOfTargets;
    
      pdebug(DFULL, "selectTargetGroup\n");
    
      targetGroup = NULL;
      *ship = 0;
    
    /* Compute the total number of targets */
      totalNumberOfTargets = 0;
      for (enemy = 0; enemy < attackingSide->noEnemies; enemy++) {
        totalNumberOfTargets += (attackingSide->enemies[enemy])->numberOfTargets;
      }
    
      if (totalNumberOfTargets > 0) {
        int             lowerLim;
        int             upperLim;
        int             n;
    
        lowerLim = 0;
    
    /* Randomly select one of the targets */
        n = frand3(totalNumberOfTargets);
    
    /* Find the owner of the target this ship */
        for (enemy = 0; enemy < attackingSide->noEnemies; enemy++) {
          upperLim = lowerLim + (attackingSide->enemies[enemy])->numberOfTargets;
          if ((n >= lowerLim) &&
              (n < upperLim)) {
            group          *aGroup;
    
            *targetSide = attackingSide->enemies[enemy];
            n -= lowerLim;
            lowerLim = 0;
    
    /* Find the group the ship belongs to, and the number of the ship  within
     * the group */
            for (aGroup = (*targetSide)->groups;
                 aGroup;
                 aGroup = aGroup->next) {
              upperLim = lowerLim + aGroup->left;
              if ((n >= lowerLim) &&
                  (n < upperLim)) {
                targetGroup = aGroup;
                *ship = n - lowerLim;
                return targetGroup;
              }
              lowerLim = upperLim;
            }
          }
          lowerLim = upperLim;
        }
      }
      return NULL;
    }    

GalaxyNG/shoot

NAME
   shoot -- does a ship surive a shot? 
SYNOPSIS
   int shoot(group *attacker, group *defender)
FUNCTION
   Let one ship fire an other and determine whether the ship 
   got killed.
RESULT
   TRUE -- ship got killed
   FALSE -- ship survived
NOTES
   The name of this function is wrong.
SEE ALSO
   attack()
SOURCE
    int
    shoot(group *attacker, group *defender)
    {
      double          defense;
    
      assert(attacker->type->weapons > 0);
      defense = defender->defense;
      return ((defense > 0) ?
              ((attacker->attack / defense) > pow(4.0, frand2())) : 1);
    }