This page is a discussion of the Basic Combat Module.
This page will cover the Basic Combat Module and Combat Handler. As the name suggests, the module is designed as a means of keeping track of all forms of in-game combat (including ship-to-ship engagements, planetary encounters with hostile forces, and hunting lifeforms); it will be required mainly by the Planetary Exploration and Encounter Modules. This module will handle combatant involvement, movement, range determination, hit determination and damage allocation (SFRPG's design will be heavily involved with this module). As combat will be required for the player to bring the game to a successful conclusion, it is vitally important to the game. All code for the Basic Combat Module is located in the sf3_combat_handler.py file.
Summary Description
The Combat_Handler Object is one of the data handlers called up during the game's initialization by the StarflightIIICore Object. A Combatant object is created and stored in the handler (and thus in the default Panda3d task manager) any time a Creature or Vehicle object is called into creation, usually either when lifeforms are indicated on a planet's surface (by the Planetary Exploration Module) or when a space encounter begins (by the Encounter Module). The player's ship, vehicles and crew are all also considered combatants; combatant records are created for them at the time of their creation as well. When created, the combatant object loads or creates data on all aspects of the object regarding combat (items such as hit difficulties, hit points, damage capabilities and special effects) and selects a target for each party present that is not under the direct control of the player. The module handles the results of any attack by made any party, including hit determination, damage resolution, and emotional index adjustment. The module remains active as long as the game routine is running.
Data Structure of the Combat_Handler Object¶
The following list is an indication of the various variables and methods that will be included in a Combat_Handler object. This list contains suggested methods and variables. Combat_Handler objects are meant to be used as a task managed by the default Panda3d task handler for the purpose of keeping track of combat statistics. Combat_Handler objects have one type of child object, Combatant, which is used to hold the data on specific available combatants given the current game situation. Only one Combat_Handler object is meant to be used in the game, and is meant to be loaded alongside the other basic modules loaded by the StarflightIIICore object when the game routine initializes. The object's data structure is as follows:
- Class: Combat_Handler
- Dictionary: Combatant_List
- Class: Combatant
- Class: AI (for details on the AI object, see the discussion under the Artifical Intelligence Module.)
- String: szIdentifier (used to distinguish the combatant).
- String: szScale (scale of the combatant; should be creature, character, vehicle or starship)
- String: szTarget (holds the identifier of the combatant's current target or combatant of interest).
- Flag: bActive (is the combatant active in the current combat scenario? Prevents characters and lifeforms from directly attacking starships...)
- Integer: nMaxHP (maximum hit points)
- List: vnCurHP (current hit points, one per firing arc)
- Flag: bHPAllArcs (take damage in all arcs simultaneously? Useful for lifeforms)
- Flag: bNHP (can the combatant take non-lethal damage? Good for vehicles and starships)
- Integer: nMaxNHP (maximum non-lethal hit points)
- List: vnCurNHP (current non-lethal hit points, one per firing arc)
- Flag: bNHPAllArcs (take non-lethal damage in all arcs simultaneously? Useful for lifeforms)
- Integer: nMaxSHP (maximum shield hit points)
- List: vnCurSHP (current shield hit points, one per firing arc)
- Flag: bSHPAllArcs (take shield damage in all arcs simultaneously? Not sure what this may be used for yet.)
- Integer: nMaxAHP (maximum armor hit points)
- List: vnCurAHP (current armor hit points, one per firing arc)
- Flag: bAHPAllArcs (take armor damage in all arcs simultaneously? Not sure what this may be used for yet.)
- List: vAttacks
- Tuple: Attack Info
- String: Weapon Name
- Integer: Weapon Class
- Integer: Attack Strength (Damage)
- Integer: Weapon Mass
- String: Special Effects
- Flag: bDead (Alive or Dead. Dead combatants usually will need to execute some "death" code; they'll usually be removed from the manager at the conclusion of combat.)
- Flag: bHP_Drain (Drain/No Drain; used to emulate poison, disease, and other effects)
- String: szDrain_ID (if the HP_Drain flag is set to TRUE, this will be reported as part of the creature's condition, i.e. "POISONED", "SICK", "INSANE", etc.)
- Integer: nDrain (this is the amount of HP drained from the creature if the HP_Drain is set to TRUE during an hourly Check)
- Integer: nHD (hit difficulty)
- Integer: nTHD (touch hit difficulty, used for making touch attacks on creatures. May not be needed for SF3)
- Integer: nBHD (blast hit difficulty, used with blast weapons such as blastopods and exploding mass drivers)
- Integer: nFHD (flatfoot hit difficulty, used when the target has been surprised by something.)
- Method: _init_(id, scale, active, AI)
- Method: selectTarget(szIdentifier)
- Method: determineRange(szIdentifier)
- Method: fireWeapon(weapon) (used for shooting)
- Method: nTakeDamage(amount) (used to divvy up damage)
- Method: nTakeShieldDamage(amount, arc) (used to calculate shield damage)
- Method: nTakeArmorDamage(amount, arc) (used to calculate armor damage)
- Method: tTakeBodySystemsDamage (system, amount) (method that sends systems damage back to the vehicle/creature object)
- Method: disable()
- Method: enable()
- Method: die()
- Method: healMe() (for unassisted healing)
- Method: checkMyVitality (to recalculate a lifeform's vitality levels)
- Method: _init_()
- Method: createCombatant(szIdentifier, scale, active)
- Method: removeCombatant(szIdentifier)
Data Structure of the Bullet Object
The following list is an indication of the various variables and methods that will be included in a Bullet object. This list contains suggested methods and variables. Bullet objects are meant to be used as child objects of combatant objects, called into existence whenever a combatant fires a projectile-style weapon towards their target. Bullet objects have no child objects. In a particularly fierce firefight, there may be multiple Bullet objects in the game; in general, though, they should be kept at a minimum to prevent the game from slowing down too much. Their data structure is as follows:
- Class: Bullet
- Class: SF3Actor (for more information on this object, see the discussion under the Core Module).
- String: szWeapID (weapon type identifier. Should match the data in the equipment.xml file (see the Fleet Configuration Module for a description). Used to gather data on the weapon's effects).
- String: szSource (source ID string of the combatant that shot it. This can be used to trigger a targeting switch for the intended target).
- Method: checkCollision()
- Method: deliverDamage(szIdentifier)
Procedures and Notes¶
Combat-related definitions:
- Combatant: Anyone or anything that can be involved in a combat action. In SF3, these will be vehicles, starships, lifeforms and characters. All other objects are considered non-combatants, and cannot be harmed by a combatant.
- Combatant Group: A group of allied combatants. Any combatant not in the same combatant group as another combatant is a potential target. Fleet objects are the only true combatant groups in the game.
- Round: A period of time equal to six seconds (real-time).
- Standard Action: Any action that takes exactly one-half of a round (i.e. 3 seconds) to complete.
- Full-Round Action: Any action that takes an entire round (i.e. 6 seconds) to complete.
- Free Action: Any actions that is considered automatic. Effects of free actions are usually very mundane.
- Grid: A method of conducting combat wherein there is a visual means of determining the range between two or more combatants. SF3 will utilize a "physical grid", an orthogonal array of squares of equal size. Each square is equal to one range increment. This type of grid should be relatively easy to program, with each combatant given a position within the grid. A 128x128 grid of squares should be sufficient for the bulk of combat actions (though this may be increased if necessary). The grid should be non-apparent to the player; combatants are not confined to a defined number of facings (this can change if it proves to be difficult to program).
- Movement: Any action that affects a combatant's range to all other combatants. Combat in SF3 will use the combatant's established maximum speed (in grid squares per round) in order to determine the maximum distance the combatant may move during the course of a combat round. The combatant may move up to the distance indicated by their maximum speed. Acceleration effects may be programmed in.
- Timing: When declared actions are resolved. SF3 will resolve the actions of all combatants simultaneously.
Some portions of the design document may refer to these terms. They are holdovers from SFRPG, which while containing a method for simultaneous combat is probably not the best way of doing things for SF3.
All combat will follow this general pattern:
- Determine combatant groups.
- Determine the order in which AI objects will perform their actions.
- The initial counter value of all AI objects are staggered. This should allow the AI combatants to perform their actions at various times.
- The player's actions are resolved upon request by the player.
- All combatants may perform one (and only one) action when allowed.
- If a combatant fires a weapon, a new object for that weapon is called into existence. The weapon's set of effects are taken from the master record of the ship that fired it. Combatants may not be hurt by their own weapons.
- If a combatant is hit by weapons fire, a call is made to one of the combatant object's master "take damage" method, which resolves how that damage affects the combatant.
Old Note: As another possibility, you might be allowed to pick up escape pods after you've destroyed a ship, that would have some of the surviving sentients onboard.
This can easily be handled by a specific call for starship/vehicle combatants. Look at the starship record for escape pods; roll a die (with possible results between one and the number of escape pods installed) to see how many are produced. Each one carries one lifeform.
Methods
(use this area to list the module's methods and give a description of what the method does. As the methods are written, what they do should be written out procedurally)
Combat_Handler Class Methods ¶
_init_()
This method simply creates the Combatant_List dictionary. The method returns task.cont, allowing for continuous monitoring of the objects contained in the dictionary in the task manager.
createCombatant(szIdentifier, scale, active, AI)
This method instructs the combat_handler to create a new combatant record, usually called when a combatant is brought into existence. It passes information on the combatant's identifier (this should be a player ship name, an identifier based on the name of an alien ship in a space encounter followed by a number, a vehicle type followed by a number, a character's name, or a creature's species name followed by a number), type (based on which of the object types have made the request), activity level (true or false, based on the situation of the combatant) and AI requirements (true or false, depending on whether the combatant is under the control of the player or not). Once the combatant itself is created, its data is stored as a value in the Combatant_List dictionary, with the identifier acting as its key in the dictionary.
removeCombatant(szIdentifier)
This method instructs the combat_handler to remove a combatant from its records. This can occur when the combatant dies/is destroyed primarily, but also can happen in other situations (such as when the player leaves an encounter). The id of the combatant to be removed is passed to the record; a simple del command removes its key from the dictionary, thus removing the data from the dictionary.
Combatant Class Methods
_init_(id, scale, active, AI)
This method is used to create a specific combatant record. When it is called, the initializer creates all of the holder variables present in the Combatant class. The arguments passed to the initializer will set the object's szIdentifier string, szScale string, and bActive flag. An AI object is then created, with the value of the AI flag passed along as an argument. Information on the statistics of the combatant is then retrieved from the calling combatant object's record. This will set the values of most of the remaining holder objects. Created combatants are always started as being alive with no HP drain. Before going out of scope, a call is made to the selectTarget and determineRange methods.
selectTarget(szIdentifier)
I've shot myself in the foot by removing Initiative as a combatant rating; true, it set the order of battle, which is not necessary in a real-time game. It also, however, served as the target selection mechanism. Future decisions may warrant the return of this well-tested mechanic.This method is used to select a new target for a combatant. It may or may not be called with an argument; if called with an argument, its a signal to make the indicated combatant the new target. Otherwise, its a signal to pick a new target from a list of valid possibilities at random. A combatant may never target another combatant from within its own combatant group, but any other combatant is fair game; these are all stored into a list and one is selected at random.
determineRange(szIdentifier)
This method is used to determine the combatant's current range to its target. This method should not be called if the combatant does not have a present target; this method will check that first. Once a target has been confirmed, the method simply collects the present XYZ coordinates of the combatant as well as those of the target, calculates the distance between those two points, and stores a truncated value in the combatant's record.
fireWeapon(weapon)
This method is used to activate a weapons system. When called, the method checks the current orientation of the combatant and gathers information on the type of weapon to be activated. A beam weapon is created with a flash model; the color, length and duration of the model may all be stored along with the information on the weapon itself. The firing of a projectile weapon will call a Bullet object into existence; information on who fired the weapon and how much damage it causes will be passed along to the Bullet object. In either case, the weapon is aimed along the combatant's current trajectory.
nTakeDamage(amount)
This method is designed to delegate the type and amount of damage the combatant receives. First, the method gathers information on where the combatant was hit in regards to its defensive arcs, as well as the amount of damage received. It calls and passes both pieces of information to the nTakeShieldDamage method, and receives a return value equal to the amount of damage that must still be allocated. If this amount is zero, the method goes out of scope. Otherwise, a call is made to the nTakeArmorDamage method, with the updated damage amount passed along with the appropriate defensive arc. This method also returns a value equal to the amount of damage that must still be allocated. If this amount is zero, the method goes out of scope. Otherwise, a final call is made to the takeBodySystemsDamage method before the method goes out of scope. Any additional weapons effects (such as poisons or other conditions that cause HP drain) are accounted for prior to the first call to nTakeShieldDamage.
nTakeShieldDamage(amount, arc)
This method is used to apply damage to shields, if the combatant has any. When called, the method receives information about how much damage the combatant has received and which defensive arc has received the damage. It then checks to see if the combatant has any shield hit points in that defensive arc. If not, then the method simply returns the same value received in the amount argument and goes out of scope. Otherwise, the method will check to see if the amount of damage received is greater than the amount of shield hit points available in the indicated arc. If so, the method subtracts the shield hit points from the amount indicated in the amount argument, sets the shield hit points in that arc to zero, and returns the new amount of damage. In the event the damage indicated is less than the number of shield hit points available, the routine simply subtracts the damage amount from the shield hit points, and returns zero.
nTakeArmorDamage(amount, arc)
This method is used to apply damage to armor, if the combatant has any. When called, the method receives information about how much damage the combatant has received and which defensive arc has received the damage. It then checks to see if the combatant has any armor hit points in that defensive arc. If not, then the method simply returns the same value received in the amount argument and goes out of scope. Otherwise, the method will check to see if the amount of damage received is greater than the amount of armor hit points available in the indicated arc. If so, the method subtracts the armor hit points from the amount indicated in the amount argument, sets the armor hit points in that arc to zero, and returns the new amount of damage. In the event the damage indicated is less than the number of armor hit points available, the routine simply subtracts the damage amount from the armor hit points, and returns zero.
tTakeBodySystemsDamage (amount)¶
This method is used to apply internal damage to a combatant, the final step towards resolving a weapons hit. The method first checks the combatant's type; this will be used to determine which systems are available to take internal damage. A creature object at this point will only have its HP counts remaining; damage will be subtracted from its non-lethal hit point count first, with damage proceeding to its hit point count should the non-lethal hit point count be reduced to less than zero. Once applied, the routine will check the HP count; if it is less than zero, the method calls the die method and goes out of scope. Otherwise, the non-lethal hit point count is checked; if it equals zero, the method calls the disable method and goes out of scope. Characters only have their lethal HP count; they function the same way as creature objects.
For vehicles (and starships), a system is selected at random and damage is applied to it. A system can take up to 100 points of damage before being destroyed (this can be mitigated by the Engineer's
Damage Control score); if the system takes this much damage and there is still damage left to apply, the routine selects a different system and performs the check again. This is done until all of the damage has been allocated to systems, or until the vehicle's hull has been destroyed, at which point the remaining damage is ignored and the call is made to the die method. Should the vehicle survive, information on the systems damaged and amounts are returned, and the vehicle's records and malfunction flags are affected as necessary.
disable()
This method sends a command to a combatant that causes it to become inert, without removing it from the Combat_Handler. When called, the combatant's bActive flag is set to False; this flag is checked before any AI flags are checked, so this should prevent the combatant from taking any actions. A similar effect occurs to the player; if the flag is set to False, the game will ignore keyboard commands that involve maneuvering and firing weapons.
enable()
This method sends a command to a combatant that causes it to become active after previously becoming inert. When called, the combatant's bActive flag is set to True; this flag is checked before any AI flags are checked, so this should allow the combatant to take actions. A similar effect occurs to the player; if the flag is set to True, the game will accept keyboard commands once again.
Primarily thismethod is intended for lifeforms that suddenly come awake after being stunned into unconsciousness.die()
This method causes a transformation of the combatant to a "dead" state, and removes it from the Combat_Handler method. When called, the method first checks the combatant's type. Creature objects will perform their "death" animation and sounds and their record is removed from the Combat_Handler object before going out of scope. This should leave a corpse which can still be manipulated by the player, but that should be the only remaining effect. Vehicles and starship will perform their "explode" animation, after which a cloud of debris will be produced. Prior removing their record from the Combat_Handler object, data on the constituents of this debris cloud will be gathered from the vehicle's record, and seeded to a nascent debris object, which will be created while the "explode" animation is playing. In this case, of course, the vehicle/starship object is removed from memory and removed from the Combat_Handler at the same time.
healMe()
This method is used solely by creatures to check to see if any natural healing has taken place or not. For details on how it is intended to work, see the discussion on character healing under the
Starship Module.
This will probably require the relocation of the vitality statistic from characters to creatures when the game is finally coded in order for it to work as intended.Bullet Class methods
init()
This method initiates a bullet object. It simply calls an SF3Actor object into existence for the mesh, and adds the data necessary on the ID of the ship that fired it and what type of weapon it is. Next, a checkCollision task is sent to the Panda3d task manager for the bullet object to watch for collisions.
checkCollision()
This method checks to see if the bullet object has collided with another object. In the event a collision is detected, the weapon's record will check if the discriminate flag for the weapon is set to true. If so, it will then check to see if the collision has occurred with a friendly object. If so, the collision is ignored. In all other cases, a call is immediately made to the deliverDamage method, passing along the ID of the object with which the collision was detected to the method. The collide flag is then checked regardless. If set to false, the method merely goes out of scope. Otherwise, calls are made to destroy the bullet object and remove its associated tasks from the task manager.
deliverDamage(szIdentifier)
This message sends a signal to a combatant that it has received damage. It simply calls the nTakeDamage method of the combatant matching the identifier passed along as an argument, and passes along the amount of damage received based on the weapon's equipment record.
XML
While the Combat_Handler object does not require any XML code on its own, combatant objects will have data records that will require the use of XML. Combatant objects, like most of the other objects in the game, are largely XML driven; this will help keep them flexible up until SF3's design is finalized (i.e. any valid combatant - vehicles, starships, creatures and characters - can be added and removed from the game freely, based upon what data is available in the XML files). There are xx XML files that may be required by a combatant object depending on the combatant's type. Those files are the "creature_records.xml" file (which is discussed in the
Planetary Exploration Module), the "personnel_data.xml" file (which is discussed in the
Starport Personnel Module), and the "craft_records.xml" file (which is discussed in the
Starport Fleet Configuration Module). For specific discussions of these files, please see the appropriate module pages.
Module Status
This is current as of July 1, 2011.This module is currently in the final design phases; while specific descriptions of the intended functions of modules have yet to be written, the remainder of the module's basic description is complete at this point. Further design work on this module has been frozen for the time being, and will remain so until I'm ready to begin method descriptions for all remaining extant modules. Again, I'm pretty sure this module will need a bit of tweaking at a minimum before it works the way its intended to. Still what's here should be sufficient for the design team to proceed when the time comes to implement combat.
NEXT: Artificial Intelligence ModulePREVIOUS: Core Module and DatabaseTOP