Jason's animation events. Also reworked entity-types system.

This was SVN commit r2209.
This commit is contained in:
MarkT 2005-05-01 19:09:13 +00:00
parent 32e3ff0921
commit db168702df
79 changed files with 485 additions and 250 deletions

View File

@ -9,10 +9,10 @@
<animation file="biped/inf_idle_spear.psa" name="Idle" speed="200"/>
<animation file="biped/walk_spearshield.psa" name="Walk" speed="700"/>
<animation file="biped/dudebuild.psa" name="Build" speed="150"/>
<animation file="biped/dudechop.psa" name="Chop" speed="150"/>
<animation file="biped/dudechop.psa" name="Chop" speed="150" event="0.35"/>
<animation file="biped/dudedeath_sword.psa" name="Death" speed="200"/>
<animation file="biped/dudedecay_sword.psa" name="Decay" speed="100"/>
<animation file="biped/inf_spear_shield_atk_a.psa" name="Attack" speed="400"/>
<animation file="biped/inf_spear_shield_atk_a.psa" name="Attack" speed="400" event="0.10"/>
<animation file="biped/inf_spear_shield_atk_b.psa" name="Attack1" speed="400"/>
</animations>
<mesh>skeletal/m_pants_c.pmd</mesh>

View File

@ -9,10 +9,10 @@
<animation file="biped/inf_idle_spear.psa" name="Idle" speed="200"/>
<animation file="biped/walk_spearshield.psa" name="Walk" speed="700"/>
<animation file="biped/dudebuild.psa" name="Build" speed="150"/>
<animation file="biped/dudechop.psa" name="Chop" speed="150"/>
<animation file="biped/dudechop.psa" name="Chop" speed="150" event="0.35"/>
<animation file="biped/dudedeath_sword.psa" name="Death" speed="200"/>
<animation file="biped/dudedecay_sword.psa" name="Decay" speed="100"/>
<animation file="biped/inf_spear_shield_atk_a.psa" name="Attack" speed="400"/>
<animation file="biped/inf_spear_shield_atk_a.psa" name="Attack" speed="400" event="0.10"/>
<animation file="biped/inf_spear_shield_atk_b.psa" name="Attack1" speed="400"/>
</animations>
<mesh>skeletal/m_pants_c.pmd</mesh>

View File

@ -9,10 +9,10 @@
<animation file="biped/inf_idle_spear.psa" name="Idle" speed="200"/>
<animation file="biped/walk_spearshield.psa" name="Walk" speed="700"/>
<animation file="biped/dudebuild.psa" name="Build" speed="150"/>
<animation file="biped/dudechop.psa" name="Chop" speed="150"/>
<animation file="biped/dudechop.psa" name="Chop" speed="150" event="0.35"/>
<animation file="biped/dudedeath_sword.psa" name="Death" speed="200"/>
<animation file="biped/dudedecay_sword.psa" name="Decay" speed="100"/>
<animation file="biped/inf_sword_shield_atk_a.psa" name="Attack" speed="400"/>
<animation file="biped/inf_sword_shield_atk_a.psa" name="Attack" speed="400" event="0.10"/>
<animation file="biped/inf_sword_shield_atk_b.psa" name="Attack1" speed="400"/>
<animation file="biped/inf_sword_shield_atk_c.psa" name="Attack2" speed="400"/>
</animations>

View File

@ -12,8 +12,7 @@
version="0.1 pasap2"
class1="Gaia"
type.gaia="true"
type.gaia.group="true"
classes="gaia"
/>
</Traits>
</Entity>

View File

@ -6,7 +6,7 @@
generic="Generic Fauna"
class2="Fauna"
type.gaia.group.fauna="true"
classes="gaia_fauna"
/>
<MiniMap
type="Food"

View File

@ -6,7 +6,7 @@
generic="Generic Flora"
class2="Flora"
type.gaia.group.flora="true"
classes="gaia_flora"
/>
</Traits>
<Footprint Radius="1.0"/>

View File

@ -8,8 +8,8 @@
icon="flora_deciduotree"
type.gaia.group.resource="true"
classes="gaia_resource"
rollover="Foragers can gather the fruit from these bushes to accumulate Food."
history="Berries are tasty, fruity, and scrunchiously crunchable."

View File

@ -6,7 +6,7 @@
generic="Tree"
class3="Tree"
type.gaia.group.resource="true"
classes="gaia_resource"
rollover="Chop these down to accumulate Wood."
/>

View File

@ -6,7 +6,7 @@
generic="Generic Geology"
class2="Geo"
type.gaia.group.geo="true"
classes="gaia_geo"
/>
</Traits>
<Footprint Radius="1.0"/>

View File

@ -6,6 +6,7 @@
generic="Generic Special"
class2="Special"
classes="gaia_special"
type.gaia.group.special="true"
/>
<MiniMap

View File

@ -15,10 +15,6 @@
/>
</Traits>
<Actions>
<Move
speed="0"
turningRadius="0"
/>
</Actions>
<Footprint Radius="1.0"/>
</Entity>

View File

@ -10,8 +10,6 @@
version="0.1 pasap2"
class1="Other"
type.other="true"
type.other.group="true"
/>
</Traits>
</Entity>

View File

@ -6,7 +6,7 @@
generic="Generic Projectile"
class2="Projectile"
type.other.group.projectile="true"
classes="projectile"
/>
</Traits>
</Entity>

View File

@ -6,7 +6,7 @@
generic="Generic Special"
class2="Special"
type.other.group.special="true"
classes="special"
/>
</Traits>
</Entity>

View File

@ -11,10 +11,7 @@
version="0.1 pasap2"
class1="Structure"
type.structure="true"
type.structure.group="true"
type.structure.phase="true"
type.structure.material="true"
classes="structure"
/>
</Traits>
</Entity>

View File

@ -9,8 +9,7 @@
rollover="The heart of the player's empire. Trains economic units, and can only be built on settlements."
type.structure.group.housing="true"
type.structure.phase.vp="true"
classes="structure_housing, structure_village"
/>
<Garrison
curr="0"

View File

@ -9,7 +9,7 @@
rollover="The Temple trains physicians, researches medical technologies, and provides healing to nearby allied organic units."
type.structure.phase.tp="true"
classes="structure_town"
/>
<Health
curr="600"

View File

@ -9,8 +9,7 @@
rollover="A civic building that increases the player's population."
type.structure.group.housing="true"
type.structure.phase.vp="true"
classes="structure_housing, structure_village"
/>
<Health
curr="250"

View File

@ -9,7 +9,7 @@
rollover="Outposts can be built anywhere on a player's land, and garrisoned to provide reconnaissance and defense against attackers."
type.structure.phase.vp="true"
classes="structure_village"
/>
<Garrison
curr="0"

View File

@ -9,7 +9,7 @@
rollover="Build walls to protect a town, close off choke points, and defend resource sites."
type.structure.phase.vp="true"
classes="structure_village"
/>
<Health
curr="500"

View File

@ -9,7 +9,7 @@
rollover="Gates allow allied units access through a wall, but are also its most vulnerable point."
type.structure.phase.vp="true"
classes="structure_village"
/>
<Health
curr="100"

View File

@ -9,7 +9,7 @@
rollover="Towers can be garrisoned to defend a city wall against attackers."
type.structure.phase.vp="true"
classes="structure_village"
/>
<Garrison
curr="0"

View File

@ -9,7 +9,7 @@
rollover="The Farmstead is the centre for food gathering operations. Fields and Corrals must be built at lots adjacent to it."
type.structure.phase.vp="true"
classes="structure_village"
/>
<Garrison
curr="0"

View File

@ -9,7 +9,7 @@
rollover="A Mill must be constructed near a source of Wood, Stone or Ore in order to collect them. It also provides technologies that improve gathering of these resources."
type.structure.phase.vp="true"
classes="structure_village"
/>
<Health
curr="50"

View File

@ -9,7 +9,7 @@
rollover="The Market creates trade units and allows resources to be bartered. Traders travel between Markets, generating resources on a successful round-trip."
type.structure.phase.tp="true"
classes="structure_town"
/>
<Health
curr="600"

View File

@ -9,7 +9,7 @@
rollover="The Fortress represents the pinnacle of a civilisation's military prowess. Heroes, powerful advanced units, and unique technologies are available from this structure."
type.structure.phase.cp="true"
classes="structure_city"
/>
<Garrison
curr="0"

View File

@ -9,7 +9,7 @@
rollover="The Barracks trains military units. Technologies may also be researched to improve their equipment."
type.structure.phase.tp="true"
classes="structure_town"
/>
<Health
curr="600"

View File

@ -9,7 +9,7 @@
rollover="Naval vessels are manufactured at this building. A Dock must be built on the shoreline of a body of water."
type.structure.phase.tp="true"
classes="structure_town"
/>
<Health
curr="600"

View File

@ -9,7 +9,7 @@
rollover="This is a special building unique to a particular civilisation."
type.structure.phase.cp="true"
classes="structure_city"
/>
</Traits>
<Footprint Width="24.0" Height="24.0"/>

View File

@ -9,7 +9,7 @@
rollover="Herd domestic animals into a Corral, and they will provide Food for your civilisation."
type.structure.phase.vp="true"
classes="structure_village"
/>
<Garrison
curr="0"

View File

@ -9,7 +9,7 @@
rollover="Assign an economic unit to a Field to harvest grain and generate Food."
type.structure.phase.vp="true"
classes="structure_village"
/>
<Garrison
curr="0"

View File

@ -3,7 +3,6 @@
<Entity>
<Traits extant="true" corpse="template_corpse">
<Id
type="true"
/>
<!-- Defaults to no armour -->
<Armour

View File

@ -13,10 +13,7 @@
personal=""
class1="Unit"
type.unit="true"
type.unit.group="true"
type.unit.material="true"
type.unit.attack="true"
classes="unit"
/>
<Audio
path="audio/voice/hellenes/soldier"

View File

@ -10,13 +10,7 @@
personal1="male_names_1st.csv"
personal2="male_names_2nd.csv"
type.unit.group.military="true"
type.unit.mounted="true"
type.unit.weapon="true"
type.unit.material.organic="true"
type.unit.group.citizensoldier="true"
type.unit.group.worker="true"
classes="unit_cavalry, unit_military, unit_mounted, unit_organic, unit_citizensoldier, unit_worker"
/>
<!-- Should stop 3 hack, 1 pierce damage... -->
<Armour

View File

@ -7,7 +7,7 @@
class3="Melee"
type.unit.attack.melee="true"
classes="unit_melee"
/>
</Traits>
</Entity>

View File

@ -7,10 +7,9 @@
icon_cell="15"
type.unit.mounted.spear="true"
type.unit.weapon.spear="true"
classes="unit_mounted_spear, unit_spear"
rollover="This was the weapon of choice from horseback. This was the unit that would eventually evolve to the medieval knight (after heavy influence by the Sarmartians and their lances). Infantry were an easy kill; just ride them down and skewer them with your stick and its point on the end. As with all cavalry it was only the rich and the nobles who were able to fight from horseback due to the cost of owning such a beast."
rollover="This was the weapon of choice from horseback. This was the unit that would eventually evolve to the medieval knight (after heavy influence by the Sarmartians and their lances). Infantry were an easy kill; just ride them down and skewer them with your stick and its point on the end. As with all cavalry - it was only the rich and the nobles who were able to fight from horseback due to the cost of owning such a beast."
/>
</Traits>
</Entity>

View File

@ -7,10 +7,9 @@
icon_cell="18"
type.unit.mounted.sword="true"
type.unit.weapon.sword="true"
classes="unit_mounted_sword, unit_sword"
rollover="Fighting from horseback with a sword is a tricky thing to do. This required usage of a sword that was longer than the typical infantry sword. One needed a good reach to attack from the height of a horse. If you were without spear (the ideal weapon of choice) it was probably because you needed your hands free to do other tasks such as riding hard and fast. It wasnt uncommon for the men to dismount and attack from foot if they were armed with only a sword."
rollover="Fighting from horseback with a sword is a tricky thing to do. This required usage of a sword that was longer than the typical infantry sword. One needed a good reach to attack from the height of a horse. If you were without spear (the ideal weapon of choice) it was probably because you needed your hands free to do other tasks such as riding hard and fast. It wasn't uncommon for the men to dismount and attack from foot if they were armed with only a sword."
/>
</Traits>
</Entity>

View File

@ -6,8 +6,8 @@
generic="Generic Ranged Cavalry Unit"
class3="Ranged"
type.unit.attack.ranged="true"
classes="unit_ranged"
/>
</Traits>
</Entity>

View File

@ -7,10 +7,9 @@
icon_cell="24"
type.unit.mounted.bow="true"
type.unit.weapon.bow="true"
rollover="A very rare unit in Part 1. It was used by the Persians, but it didn’t gain much traction until the Parthians, Huns, Mongols, and other people of the nomadic steeps introduced them to western Europe. This was the most effective unit on the battlefield for several hundred years until the well armoured knight came along. Therefore, this unit will gain much more prominence in Part 2."
classes="unit_mounted_bow, unit_bow"
rollover="A very rare unit in Part 1. It was used by the Persians, but it didn't gain much traction until the Parthians, Huns, Mongols, and other people of the nomadic steeps introduced them to western Europe. This was the most effective unit on the battlefield for several hundred years until the well armoured knight came along. Therefore, this unit will gain much more prominence in Part 2."
/>
</Traits>
</Entity>

View File

@ -7,10 +7,9 @@
icon_cell="21"
type.unit.mounted.javelin="true"
type.unit.weapon.javelin="true"
rollover="The javelins thrown from a horse’s back were probably 3 at most. The idea was to quickly advance with 3 in hand, then throw them all. After you had done that it was time to switch to your secondary weapon which was usually a spear or a sword. However, in the game - these units will only have ranged attack."
classes="unit_mounted_javelin, unit_javelin"
rollover="The javelins thrown from a horse's back were probably 3 at most. The idea was to quickly advance with 3 in hand, then throw them all. After you had done that it was time to switch to your secondary weapon which was usually a spear or a sword. However, in the game - these units will only have ranged attack."
/>
</Traits>
</Entity>

View File

@ -7,9 +7,7 @@
class2="Hero"
type.unit.group.military="true"
type.unit.group.hero="true"
type.unit.material.organic="true"
classes="unit_military, unit_hero, unit_organic"
/>
<Loot
up="1000"

View File

@ -10,13 +10,7 @@
personal1="male_names_1st.csv"
personal2="male_names_2nd.csv"
type.unit.group.military="true"
type.unit.foot="true"
type.unit.weapon="true"
type.unit.material.organic="true"
type.unit.group.citizensoldier="true"
type.unit.group.worker="true"
classes="unit_military, unit_foot, unit_organic, unit_citizensoldier, unit_worker"
/>
<!-- Should stop 3 hack, 1 pierce damage... -->
<Armour
@ -39,20 +33,22 @@
/>
</Traits>
<Actions>
<!-- 2 hack, 2 pierce damage... -->
<!-- 2 hack, 2 pierce damage, every 2.0 seconds -->
<Attack
range="2.0"
damage="4"
crush="0.0"
hack="0.5"
pierce="0.5"
speed="2000"
/>
<Create
list=""
list.structciv="cc;fc;ho;rc;tc;hc"
list.structmil="mc;pc;ff;tf;wc;wg;wt"
/>
<Gather>
<!-- A gather cycle is 3 seconds, in which <gather_subtype>.speed is collected -->
<Gather range="2.0" speed="3000">
<Food>
<Meat speed="1" />
<Fruit speed="1" />

View File

@ -7,7 +7,7 @@
class3="Melee"
type.unit.attack.melee="true"
classes="unit_melee"
/>
</Traits>
<Event On="Initialize">

View File

@ -7,9 +7,9 @@
icon_cell="3"
type.unit.foot.spear="true"
type.unit.weapon.spear="true"
rollover="Probably one of the most primitive units in the game. Fighting with a sharp object at the end of the pole didnt require a lot of technology to develop. It also allowed the human to distance themselves from their attacker. As time passed the spears got longer and longer. Started with a fighting style similar to using a quarterstaff, then to using them in numbers as a ‘pin cushion’ vs. humans (sarissa in a phalanx). Later it was developed to combat cavalry. During the medieval period it evolved to the pike. These units tended to be armoured as heavily as possible."
classes="unit_foot_spear, unit_spear"
rollover="Probably one of the most primitive units in the game. Fighting with a sharp object at the end of the pole didn't require a lot of technology to develop. It also allowed the human to distance themselves from their attacker. As time passed the spears got longer and longer. Started with a fighting style similar to using a quarterstaff, then to using them in numbers as a 'pin cushion' vs. humans (sarissa in a phalanx). Later it was developed to combat cavalry. During the medieval period it evolved to the pike. These units tended to be armoured as heavily as possible."
/>
</Traits>
</Entity>

View File

@ -7,8 +7,8 @@
icon_cell="0"
type.unit.foot.sword="true"
type.unit.weapon.sword="true"
classes="unit_foot_sword, unit_sword"
rollover="Weapon is basically a developed sickle. Probably from the club, to the axe, to the sickle to the sword. It was the Romans who used them to combat the long range of the sarissa. Their spears were so long they had to use two hands to wield them. In a formation they were almost impossible to maneouvre. If flanked, they were easily cut down by a sword as demonstrated by the Romans at the battle of Cynoscephalae. Generally swordsmen were well armoured, had shields, and tended to be nobles. A good sword was an expensive weapon."
/>
</Traits>

View File

@ -7,7 +7,7 @@
class3="Ranged"
type.unit.attack.ranged="true"
classes="unit_ranged"
/>
</Traits>
</Entity>

View File

@ -7,9 +7,9 @@
icon_cell="9"
type.unit.foot.bow="true"
type.unit.weapon.bow="true"
rollover="They tended to be lightly armoured. They usually only participated in the first stage of a battle, sending a volley of arrows raining down the enemy. Of course they would have to stop shooting once the melee units closed in. This means their job was largely over once the ‘true battle’ was underway. They spent hours training with a bow, but if you were hit by an arrow it was more likely an act of random chance than being specifically targeted by an archer."
classes="unit_foot_bow, unit_bow"
rollover="They tended to be lightly armoured. They usually only participated in the first stage of a battle, sending a volley of arrows raining down the enemy. Of course they would have to stop shooting once the melee units closed in. This means their job was largely over once the 'true battle' was underway. They spent hours training with a bow, but if you were hit by an arrow it was more likely an act of random chance than being specifically targeted by an archer."
/>
</Traits>
</Entity>

View File

@ -7,9 +7,9 @@
icon_cell="6"
type.unit.foot.javelin="true"
type.unit.weapon.javelin="true"
rollover="These were the skirmishers. These lightly armoured units would advance quickly, throw a hail of javelins and then retreat back to their ranks. Grab another spear and repeat. They would do well against any unit that wasnt wearing proper armour, but more poorly if they fought hand to hand vs a well armoured unit. They didnt always have to thow their spears either. They used these light small spears in hand to hand similar to a quarterstaff. Also note that the development of the pilum was a key transition. This pilum was a weapon with a long steel shaft that would sink into a shield, nearly impossible to remove. This rendered the shield useless. They also weighted and balanced them to make them accurately hit with a punch."
classes="unit_foot_javelin, unit_javelin"
rollover="These were the skirmishers. These lightly armoured units would advance quickly, throw a hail of javelins and then retreat back to their ranks. Grab another spear and repeat. They would do well against any unit that wasn't wearing proper armour, but more poorly if they fought hand to hand vs a well armoured unit. They didn't always have to thow their spears either. They used these light small spears in hand to hand similar to a quarterstaff. Also note that the development of the pilum was a key transition. This pilum was a weapon with a long steel shaft that would sink into a shield, nearly impossible to remove. This rendered the shield useless. They also weighted and balanced them to make them accurately hit with a punch."
/>
</Traits>
</Entity>

View File

@ -7,10 +7,9 @@
icon_cell="12"
type.unit.foot.sling="true"
type.unit.weapon.sling="true"
rollover="They were amazing shots with their slings. They used choice rocks, and often specifically created ‘shot’ made from lead. They could pierce armour at close distances. They were lightly armoured because they needed the mobility in their arm regions. However, in the game we are making a gameplay consideration to have slingers act as 'mini-organic-onagers' that may cause damage to structures."
classes="unit_foot_sling, unit_sling"
rollover="They were amazing shots with their slings. They used choice rocks, and often specifically created 'shot' made from lead. They could pierce armour at close distances. They were lightly armoured because they needed the mobility in their arm regions. However, in the game we are making a gameplay consideration to have slingers act as 'mini-organic-onagers' that may cause damage to structures."
/>
</Traits>
</Entity>

View File

@ -7,8 +7,7 @@
class2="Mechanical"
type.unit.group.military="true"
type.unit.material.mechanical="true"
classes="unit_military, unit_mechanical"
/>
<Loot
up="0"

View File

@ -7,7 +7,7 @@
class3="Ship"
type.unit.group.ship="true"
classes="unit_ship"
/>
</Traits>
</Entity>

View File

@ -7,7 +7,7 @@
icon_cell="39"
type.unit.group.warship="true"
classes="unit_warship"
/>
</Traits>
</Entity>

View File

@ -2,13 +2,14 @@
<Entity Parent="template_unit_mechanical_ship">
<Traits>
<!-- Note syntax for removing a class -->
<Id
generic="Merchant Ship"
icon_cell="38"
type.unit.group.military="false"
type.unit.group.trade="true"
classes="-unit_military, unit_trade"
/>
</Traits>
</Entity>

View File

@ -6,8 +6,8 @@
generic="Quinquereme"
icon_cell="41"
type.unit.group.warship="true"
classes="unit_warship"
/>
</Traits>
</Entity>

View File

@ -7,7 +7,7 @@
icon_cell="40"
type.unit.group.warship="true"
classes="unit_warship"
/>
</Traits>
</Entity>

View File

@ -7,7 +7,7 @@
class3="Siege"
type.unit.group.siege="true"
classes="unit_siege"
/>
</Traits>
</Entity>

View File

@ -7,7 +7,7 @@
icon_cell="37"
type.unit.attack.ranged="true"
classes="unit_ranged"
/>
</Traits>
</Entity>

View File

@ -7,7 +7,7 @@
icon_cell="35"
type.unit.attack.ranged="true"
classes="unit_ranged"
/>
</Traits>
</Entity>

View File

@ -7,7 +7,7 @@
icon_cell="36"
type.unit.attack.melee="true"
classes="unit_melee"
/>
</Traits>
</Entity>

View File

@ -7,8 +7,7 @@
class2="Super Unit"
type.unit.group.military="true"
type.unit.group.superunit="true"
classes="unit_military, unit_superunit"
/>
<Loot
up="50"

View File

@ -9,8 +9,7 @@
class3="Super Cavalry Unit"
type.unit.material.organic="true"
type.unit.mounted="true"
classes="unit_organic, unit_mounted"
/>
</Traits>
</Entity>

View File

@ -9,8 +9,7 @@
class3="Super Infantry Unit"
type.unit.material.organic="true"
type.unit.foot="true"
classes="unit_organic, unit_foot"
/>
</Traits>
</Entity>

View File

@ -9,8 +9,7 @@
class3="Super Siege Unit"
type.unit.material.mechanical="true"
type.unit.group.siege="true"
classes="unit_mechanical, unit_siege"
/>
</Traits>
</Entity>

View File

@ -9,7 +9,8 @@
personal2="female_names_2nd.csv"
class2="Support"
type.unit.support="true"
classes="unit_support"
/>
<Loot
up="1"

View File

@ -7,7 +7,7 @@
icon_cell="34"
type.unit.group.trade="true"
classes="unit_trade"
rollover="Trade was a very important part of ancient civilisation - effective trading and control of trade routes equaled wealth. Trade took place by many forms from foot to caravans to merchant ships. One of the most notorious examples of the power of trade was the Silk Road."
/>

View File

@ -27,12 +27,21 @@ function SelectGroup(groupNumber)
// Set the current selection to the specified group (team) number, 1-9.
// If the group is already selected, centre on the group.
console.write( groupNumber );
if (groups[groupNumber].length > 0)
{
// If group already selected,
if (selection == groups[groupNumber])
if (selection.equals( groups[groupNumber] ) )
{
setCameraTarget(selection[0].position); // Centre on it.
// Find the average position of the group
position_avg = new Vector3D();
for( t = 0; t < selection.length; t++ )
position_avg = position_avg.add( selection[t].position );
position_avg = position_avg.divide( selection.length );
setCameraTarget( position_avg ); // Centre on it.
}
else
selection = groups[groupNumber]; // If not, select it.

View File

@ -181,6 +181,11 @@ void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
transform.SetIdentity();
SetTransform(transform);
// Following seems to stomp over the current animation time - which, unsurprisingly,
// introduces artefacts in the currently playing animation. Save it here and restore it
// at the end.
float AnimTime = m_AnimTime;
// iterate through every frame of the animation
for (uint j=0;j<anim->GetNumFrames();j++) {
// extend bounds by vertex positions at the frame
@ -194,12 +199,13 @@ void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
}
SetTransform(oldtransform);
m_AnimTime = AnimTime;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BuildAnimation: load raw animation frame animation from given file, and build a
// animation specific to this model
CSkeletonAnim* CModel::BuildAnimation(const char* filename,float speed,size_t actionpos)
CSkeletonAnim* CModel::BuildAnimation(const char* filename,float speed,double actionpos)
{
CSkeletonAnimDef* def=g_SkelAnimMan.GetAnimation(filename);
if (!def) return 0;
@ -207,9 +213,7 @@ CSkeletonAnim* CModel::BuildAnimation(const char* filename,float speed,size_t ac
CSkeletonAnim* anim=new CSkeletonAnim;
anim->m_AnimDef=def;
anim->m_Speed=speed;
anim->m_ActionPos=actionpos;
if( actionpos > anim->m_AnimDef->GetDuration() )
anim->m_ActionPos = anim->m_AnimDef->GetDuration();
anim->m_ActionPos=(size_t)( actionpos * anim->m_AnimDef->GetDuration() );
anim->m_ObjectBounds.SetEmpty();
InvalidateBounds();

View File

@ -102,7 +102,7 @@ public:
// load raw animation frame animation from given file, and build a
// animation specific to this model
CSkeletonAnim* BuildAnimation(const char* filename,float speed,size_t actionpos);
CSkeletonAnim* BuildAnimation(const char* filename,float speed, double actionpos);
// add a prop to the model on the given point
void AddProp(SPropPoint* point,CModel* model);

View File

@ -159,7 +159,7 @@ bool CObjectBase::Load(const char* filename)
AT(file);
AT(name);
AT(speed);
AT(actionpos);
AT(event);
AT(attachpoint);
AT(actor);
AT(frequency);
@ -220,10 +220,12 @@ bool CObjectBase::Load(const char* filename)
anim.m_Speed = CStr(ae.Value).ToInt() / 100.f;
if (anim.m_Speed <= 0.0) anim.m_Speed = 1.0f;
}
else if (ae.Name == at_actionpos)
else if (ae.Name == at_event)
{
anim.m_ActionPos = CStr(ae.Value).ToInt();
if (anim.m_ActionPos < 0) anim.m_ActionPos = 0;
anim.m_ActionPos = CStr(ae.Value).ToDouble();
if (anim.m_ActionPos < 0.0) anim.m_ActionPos = 0.0;
else if (anim.m_ActionPos > 100.0) anim.m_ActionPos = 1.0;
else if (anim.m_ActionPos > 1.0) anim.m_ActionPos /= 100.0;
}
else
; // unrecognised element

View File

@ -13,7 +13,7 @@ public:
struct Anim {
// constructor
Anim() : m_Speed(1), m_ActionPos( 0 ), m_AnimData(0) {}
Anim() : m_Speed(1), m_ActionPos( 0.0 ), m_AnimData(0) {}
// name of the animation - "Idle", "Run", etc
CStr m_AnimName;
@ -21,8 +21,10 @@ public:
CStr m_FileName;
// animation speed, as specified in XML actor file
float m_Speed;
// time during the animation at which the interesting bit happens (msec)
size_t m_ActionPos;
// fraction of the way through the animation that the interesting bit
// happens (this is converted to an absolute time when the animation
// data is loaded)
double m_ActionPos;
// the animation data, specific to the this model
CSkeletonAnim* m_AnimData;
};

View File

@ -62,12 +62,15 @@ extern float fmaxf(float a, float b);
// C++ linkage
// STL_HASH_MAP, STL_HASH_MULTIMAP
// STL_HASH_MAP, STL_HASH_MULTIMAP, STL_HASH_SET
#ifdef __GNUC__
// GCC
# include <ext/hash_map>
# include <ext/hash_set> // Probably?
# define STL_HASH_MAP __gnu_cxx::hash_map
# define STL_HASH_MULTIMAP __gnu_cxx::hash_multimap
# define STL_HASH_SET __gnu_cxx::hash_set
// Hack: GCC Doesn't have a hash instance for std::string included (and it looks
// like they won't add it - marked resolved/wontfix in the gcc bugzilla)
@ -84,14 +87,17 @@ namespace __gnu_cxx
#else // !__GNUC__
# include <hash_map>
# include <hash_set>
# if defined(_MSC_VER) && (_MSC_VER >= 1300)
// VC7 or above
# define STL_HASH_MAP stdext::hash_map
# define STL_HASH_MULTIMAP stdext::hash_multimap
# define STL_HASH_SET stdext::hash_set
# else
// VC6 and anything else (most likely name)
# define STL_HASH_MAP std::hash_map
# define STL_HASH_MULTIMAP std::hash_multimap
# define STL_HASH_SET std::hash_set
# endif // defined(_MSC_VER) && (_MSC_VER >= 1300)
#endif // !__GNUC__

View File

@ -37,7 +37,8 @@ private:
static JSBool Remove( JSContext* cx, JSObject* obj, uintN argc, jsval* agv, jsval* rval );
static JSBool GetLength( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
static JSBool IsEmpty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
static JSBool Clear( JSContext* cx, JSObject* obj, uintN argc, jsval* agv, jsval* rval );
static JSBool Clear( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval );
static JSBool Equals( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval );
static JSBool AddProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
static JSBool RemoveProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
static JSBool GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
@ -77,12 +78,13 @@ template<typename T, JSClass* ScriptType> JSFunctionSpec CJSCollection<T, Script
{ "pop", Pop, 0, 0, 0 },
{ "remove", Remove, 1, 0, 0 },
{ "clear", Clear, 0, 0, 0 },
{ "equals", Equals, 1, 0, 0 },
{ 0 },
};
template<typename T, JSClass* ScriptType> std::vector<T>* CJSCollection<T, ScriptType>::RetrieveSet( JSContext* cx, JSObject* obj )
{
CJSCollectionData* Info = (CJSCollectionData*)JS_GetPrivate( cx, obj );
CJSCollectionData* Info = (CJSCollectionData*)JS_GetInstancePrivate( cx, obj, &JSI_class, NULL );
if( !Info ) return( NULL );
return( Info->m_Data );
}
@ -272,6 +274,43 @@ template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::I
return( JS_TRUE );
}
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::Equals( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
{
std::vector<T>* a = RetrieveSet( cx, obj );
if( !a )
return( JS_FALSE );
if( ( argc == 0 ) || ( !JSVAL_IS_OBJECT( argv[0] ) ) ) return( JS_FALSE );
std::vector<T>* b = RetrieveSet( cx, JSVAL_TO_OBJECT( argv[0] ) );
if( !b )
return( JS_FALSE );
std::vector<T>::iterator ita, itb;
size_t seek = a->size();
for( ita = a->begin(); ita != a->end(); ita++ )
for( itb = b->begin(); itb != b->end(); itb++ )
if( *ita == *itb ) { seek--; break; }
if( seek )
{
*rval = JSVAL_FALSE;
return( JS_TRUE );
}
seek = b->size();
for( itb = b->begin(); itb != b->end(); itb++ )
for( ita = a->begin(); ita != a->end(); ita++ )
if( *ita == *itb ) { seek--; break; }
if( seek )
{
*rval = JSVAL_FALSE;
return( JS_TRUE );
}
*rval = JSVAL_TRUE;
return( JS_TRUE );
}
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::Subset( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
{
assert( argc > 0 );

View File

@ -78,6 +78,9 @@ public:
// Rebuild any intrinsic (mapped-to-C++-variable) properties
virtual void Rebuild() = 0;
// HACK: Doesn't belong here.
virtual void rebuildClassSet() = 0;
// Check for a property
virtual IJSComplexProperty* HasProperty( CStrW PropertyName ) = 0;

View File

@ -17,8 +17,12 @@ CBaseEntity::CBaseEntity()
AddProperty( L"parent", &m_base, false );
AddProperty( L"actions.move.speed", &m_speed );
AddProperty( L"actions.move.turningradius", &m_turningRadius );
AddProperty( L"actions.attack.range", &m_meleeRange );
AddProperty( L"actions.attack.rangemin", &m_meleeRangeMin );
AddProperty( L"actions.attack.range", &( m_melee.m_MaxRange ) );
AddProperty( L"actions.attack.rangemin", &( m_melee.m_MinRange ) );
AddProperty( L"actions.attack.speed", &( m_melee.m_Speed ) );
AddProperty( L"actions.gather.range", &( m_gather.m_MaxRange ) );
AddProperty( L"actions.gather.rangemin", &( m_gather.m_MinRange ) );
AddProperty( L"actions.gather.speed", &( m_gather.m_Speed ) );
AddProperty( L"actor", &m_actorName );
AddProperty( L"traits.extant", &m_extant );
AddProperty( L"traits.corpse", &m_corpse );
@ -30,7 +34,7 @@ CBaseEntity::CBaseEntity()
}
// Initialize, make life a little easier on the scriptors
m_speed = m_turningRadius = m_meleeRange = m_meleeRangeMin = 0.0f;
m_speed = m_turningRadius = 0.0f;
m_extant = true; m_corpse = CStrW();
m_bound_type = CBoundingObject::BOUND_NONE;
@ -65,9 +69,76 @@ void CBaseEntity::loadBase()
}
SetBase( m_base );
m_classes.SetParent( &( m_base->m_classes ) );
SetNextObject( m_base );
}
jsval CBaseEntity::getClassSet()
{
STL_HASH_SET<CStrW, CStrW_hash_compare>::iterator it;
it = m_classes.m_Set.begin();
CStrW result = *( it++ );
for( ; it != m_classes.m_Set.end(); it++ )
result += L" " + *it;
return( ToJSVal( result ) );
}
void CBaseEntity::setClassSet( jsval value )
{
// Get the set that was passed in.
CStr temp = ToPrimitive<CStrW>( value );
CStr entry;
m_classes.m_Added.clear();
m_classes.m_Removed.clear();
while( true )
{
long brk_sp = temp.Find( ' ' );
long brk_cm = temp.Find( ',' );
long brk = ( brk_sp == -1 ) ? brk_cm : ( brk_cm == -1 ) ? brk_sp : ( brk_sp < brk_cm ) ? brk_sp : brk_cm;
if( brk == -1 )
{
entry = temp;
}
else
{
entry = temp.GetSubstring( 0, brk );
temp = temp.GetSubstring( brk + 1, temp.Length() );
}
if( brk != 0 )
{
if( entry[0] == '-' )
{
entry = entry.GetSubstring( 1, entry.Length() );
if( entry.Length() )
m_classes.m_Removed.push_back( entry );
}
else
{
if( entry[0] == '+' )
entry = entry.GetSubstring( 1, entry.Length() );
if( entry.Length() )
m_classes.m_Added.push_back( entry );
}
}
if( brk == -1 ) break;
}
rebuildClassSet();
}
void CBaseEntity::rebuildClassSet()
{
m_classes.Rebuild();
InheritorsList::iterator it;
for( it = m_Inheritors.begin(); it != m_Inheritors.end(); it++ )
(*it)->rebuildClassSet();
}
bool CBaseEntity::loadXML( CStr filename )
{
CXeromyces XeroFile;
@ -267,6 +338,7 @@ void CBaseEntity::XMLLoadProperty( const CXeromyces& XeroFile, const XMBElement&
void CBaseEntity::ScriptingInit()
{
AddMethod<jsval, &CBaseEntity::ToString>( "toString", 0 );
AddClassProperty( L"traits.id.classes", (GetFn)getClassSet, (SetFn)setClassSet );
CJSComplex<CBaseEntity>::ScriptingInit( "EntityTemplate" );
}

View File

@ -13,7 +13,6 @@
// properties such as position, current HP, etc. cannot be inherited from
// a template in this way.
//
// Note: Data-inheritance is not currently implemented.
#ifndef BASE_ENTITY_INCLUDED
#define BASE_ENTITY_INCLUDED
@ -24,6 +23,7 @@
#include "scripting/ScriptableComplex.h"
#include "BoundingObjects.h"
#include "EventHandlers.h"
#include "EntitySupport.h"
#include "ScriptObject.h"
#include "Xeromyces.h"
@ -43,6 +43,9 @@ public:
CStrW m_corpse;
bool m_extant;
// The class types this entity has
SClassSet m_classes;
CStrW m_Base_Name; // <- We don't guarantee the order XML files will be loaded in, so we'll store the name of the
// parent entity referenced, then, after all files are loaded, attempt to match names to objects.
@ -55,13 +58,16 @@ public:
CBoundingObject::EBoundingType m_bound_type;
float m_speed;
float m_meleeRange;
float m_meleeRangeMin;
SEntityAction m_melee;
SEntityAction m_gather;
float m_turningRadius;
CScriptObject m_EventHandlers[EVENT_LAST];
void loadBase();
jsval getClassSet();
void setClassSet( jsval value );
void rebuildClassSet();
// Script-bound functions

View File

@ -34,8 +34,12 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
AddProperty( L"traits.extant", &m_extant );
AddProperty( L"traits.corpse", &m_corpse );
AddProperty( L"actions.move.turningradius", &m_turningRadius );
AddProperty( L"actions.attack.range", &m_meleeRange );
AddProperty( L"actions.attack.rangemin", &m_meleeRangeMin );
AddProperty( L"actions.attack.range", &( m_melee.m_MaxRange ) );
AddProperty( L"actions.attack.rangemin", &( m_melee.m_MinRange ) );
AddProperty( L"actions.attack.speed", &( m_melee.m_Speed ) );
AddProperty( L"actions.gather.range", &( m_gather.m_MaxRange ) );
AddProperty( L"actions.gather.rangemin", &( m_gather.m_MinRange ) );
AddProperty( L"actions.gather.speed", &( m_gather.m_Speed ) );
AddProperty( L"position", &m_graphics_position, false, (NotifyFn)&CEntity::teleport );
AddProperty( L"orientation", &m_graphics_orientation, false, (NotifyFn)&CEntity::reorient );
AddProperty( L"player", &m_player );
@ -52,6 +56,7 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
m_lastState = -1;
m_transition = true;
m_fsm_cyclepos = NOT_IN_CYCLE;
m_base = base;
@ -105,6 +110,7 @@ void CEntity::loadBase()
// Set up our instance data
SetBase( m_base );
m_classes.SetParent( &( m_base->m_classes ) );
SetNextObject( m_base );
if( m_base->m_bound_type == CBoundingObject::BOUND_CIRCLE )
@ -168,6 +174,76 @@ void CEntity::snapToGround()
m_graphics_position.Y = pTerrain->getExactGroundLevel( m_graphics_position.X, m_graphics_position.Z );
}
jsval CEntity::getClassSet()
{
STL_HASH_SET<CStrW, CStrW_hash_compare>::iterator it;
it = m_classes.m_Set.begin();
CStrW result = L"";
if( it != m_classes.m_Set.end() )
{
result = *( it++ );
for( ; it != m_classes.m_Set.end(); it++ )
result += L" " + *it;
}
return( ToJSVal( result ) );
}
void CEntity::setClassSet( jsval value )
{
// Get the set that was passed in.
CStr temp = ToPrimitive<CStrW>( value );
CStr entry;
m_classes.m_Added.clear();
m_classes.m_Removed.clear();
while( true )
{
long brk_sp = temp.Find( ' ' );
long brk_cm = temp.Find( ',' );
long brk = ( brk_sp == -1 ) ? brk_cm : ( brk_cm == -1 ) ? brk_sp : ( brk_sp < brk_cm ) ? brk_sp : brk_cm;
if( brk == -1 )
{
entry = temp;
}
else
{
entry = temp.GetSubstring( 0, brk );
temp = temp.GetSubstring( brk + 1, temp.Length() );
}
if( brk != 0 )
{
if( entry[0] == '-' )
{
entry = entry.GetSubstring( 1, entry.Length() );
if( entry.Length() )
m_classes.m_Removed.push_back( entry );
}
else
{
if( entry[0] == '+' )
entry = entry.GetSubstring( 1, entry.Length() );
if( entry.Length() )
m_classes.m_Added.push_back( entry );
}
}
if( brk == -1 ) break;
}
rebuildClassSet();
}
void CEntity::rebuildClassSet()
{
m_classes.Rebuild();
InheritorsList::iterator it;
for( it = m_Inheritors.begin(); it != m_Inheritors.end(); it++ )
(*it)->rebuildClassSet();
}
void CEntity::update( size_t timestep )
{
m_position_previous = m_position;
@ -183,10 +259,11 @@ void CEntity::update( size_t timestep )
{
CEntityOrder* current = &m_orderQueue.front();
m_transition = ( current->m_type != m_lastState );
if( m_transition )
if( current->m_type != m_lastState )
{
m_transition = true;
m_fsm_cyclepos = NOT_IN_CYCLE;
PROFILE( "state transition / order" );
CEntity* target = NULL;
@ -210,6 +287,8 @@ void CEntity::update( size_t timestep )
m_lastState = current->m_type;
}
else
m_transition = false;
switch( current->m_type )
{
@ -288,48 +367,6 @@ void CEntity::Damage( CDamageType& damage, CEntity* inflictor )
DispatchEvent( &evt );
}
/*
void CEntity::dispatch( const CMessage* msg )
{
switch( msg->type )
{
case CMessage::EMSG_TICK:
{
CEventTick Tick;
DispatchEvent( &Tick );
break;
}
case CMessage::EMSG_INIT:
{
CEventInitialize Init;
if( !DispatchEvent( &Init ) )
break;
if( m_base->m_Tag == CStrW( L"Prometheus Dude" ) )
{
if( getCollisionObject( this ) )
{
// Prometheus telefragging. (Appeared inside another object)
kill();
return;
}
}
break;
}
case CMessage::EMSG_ORDER:
CMessageOrder* m;
m = (CMessageOrder*)msg;
if( !m->queue )
clearOrders();
pushOrder( m->order );
break;
case CMessage::EMSG_DAMAGE:
CEntityOrder* o;
}
}
*/
void CEntity::clearOrders()
{
m_orderQueue.clear();
@ -607,9 +644,11 @@ void CEntity::ScriptingInit()
AddMethod<bool, &CEntity::Kill>( "kill", 0 );
AddMethod<bool, &CEntity::Damage>( "damage", 1 );
AddMethod<bool, &CEntity::IsIdle>( "isIdle", 0 );
AddMethod<bool, &CEntity::HasClass>( "hasClass", 1 );
AddMethod<jsval, &CEntity::GetSpawnPoint>( "getSpawnPoint", 1 );
AddClassProperty( L"template", (CBaseEntity* CEntity::*)&CEntity::m_base, false, (NotifyFn)&CEntity::loadBase );
AddClassProperty( L"traits.id.classes", (GetFn)getClassSet, (SetFn)setClassSet );
CJSComplex<CEntity>::ScriptingInit( "Entity", Construct, 2 );
}

View File

@ -66,10 +66,13 @@ public:
// The entity to switch to when this dies.
CStrW m_corpse;
// The class types this entity has
SClassSet m_classes;
float m_speed;
float m_turningRadius;
float m_meleeRange;
float m_meleeRangeMin;
SEntityAction m_melee;
SEntityAction m_gather;
bool m_selected;
i32 m_grouped;
@ -99,10 +102,6 @@ public:
// Get script execution contexts - always run in the context of the entity that fired it.
JSObject* GetScriptExecContext( IEventTarget* target ) { return( ((CEntity*)target)->GetScript() ); }
// EventListener adaptation?
//typedef std::vector<CScriptObject> ExtendedHandlerList;
//typedef STL_HASH_MAP<CStrW, HandlerList, CStrW_hash_compare> ExtendedHandlerTable;
CScriptObject m_EventHandlers[EVENT_LAST];
CUnit* m_actor;
@ -110,16 +109,23 @@ public:
// State transition in the FSM (animations should be reset)
bool m_transition;
int m_lastState;
// Position in the current state's cycle
static const size_t NOT_IN_CYCLE = -1;
size_t m_fsm_cyclepos; // -cycle_length....cycle_length
CSkeletonAnim* m_fsm_animation; // the animation we're about to play this cycle,
size_t m_fsm_anipos; // the time at which we should start playing it.
size_t m_fsm_anipos2; // for when there are two animation-related events we need to take care of.
std::deque<CEntityOrder> m_orderQueue;
private:
CEntity( CBaseEntity* base, CVector3D position, float orientation );
/*EGotoSituation*/ uint processGotoHelper( CEntityOrder* current, size_t timestep_milli, HEntity& collide );
uint processGotoHelper( CEntityOrder* current, size_t timestep_milli, HEntity& collide );
bool processContactAction( CEntityOrder* current, size_t timestep_millis, int transition, float range );
bool processContactActionNoPathing( CEntityOrder* current, size_t timestep_millis, CSkeletonAnim* animation, CScriptEvent* contactEvent, float range, float minRange );
bool processContactAction( CEntityOrder* current, size_t timestep_millis, int transition, SEntityAction* action );
bool processContactActionNoPathing( CEntityOrder* current, size_t timestep_millis, CSkeletonAnim* animation, CScriptEvent* contactEvent, SEntityAction* action );
bool processAttackMelee( CEntityOrder* current, size_t timestep_milli );
bool processAttackMeleeNoPathing( CEntityOrder* current, size_t timestep_milli );
@ -162,6 +168,11 @@ public:
void snapToGround();
void updateActorTransforms();
// Getter and setter for the class sets
jsval getClassSet();
void setClassSet( jsval value );
void rebuildClassSet();
// Things like selection circles and debug info - possibly move to gui if/when it becomes responsible for (and capable of) it.
void render();
void renderSelectionOutline( float alpha = 1.0f );
@ -207,6 +218,11 @@ public:
{
return( m_orderQueue.empty() );
}
bool HasClass( JSContext* cx, uintN argc, jsval* argv )
{
assert( argc >= 1 );
return( m_classes.IsMember( ToPrimitive<CStrW>( cx, argv[0] ) ) );
}
static void ScriptingInit();
};

View File

@ -269,7 +269,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli
}
// Handles processing common to (at the moment) gather and melee attack actions
bool CEntity::processContactAction( CEntityOrder* current, size_t timestep_millis, int transition, float range )
bool CEntity::processContactAction( CEntityOrder* current, size_t timestep_millis, int transition, SEntityAction* action )
{
m_orderQueue.pop_front();
@ -278,7 +278,7 @@ bool CEntity::processContactAction( CEntityOrder* current, size_t timestep_milli
current->m_data[0].location = current->m_data[0].entity->m_position;
if( ( current->m_data[0].location - m_position ).length() < m_meleeRange )
if( ( current->m_data[0].location - m_position ).length() < action->m_MaxRange )
{
(int&)current->m_type = transition;
return( true );
@ -297,9 +297,45 @@ bool CEntity::processContactAction( CEntityOrder* current, size_t timestep_milli
return( true );
}
bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t timestep_millis, CSkeletonAnim* animation, CScriptEvent* contactEvent, float range, float minRange = 0.0f )
bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t timestep_millis, CSkeletonAnim* animation, CScriptEvent* contactEvent, SEntityAction* action )
{
static size_t cyclepos = 0;
if( m_fsm_cyclepos != NOT_IN_CYCLE )
{
size_t nextpos = m_fsm_cyclepos + timestep_millis * 2;
if( ( m_fsm_cyclepos <= m_fsm_anipos ) &&
( nextpos > m_fsm_anipos ) )
{
// Start playing.
// Start the animation. Actual damage/gather will be done in a
// few hundred ms, at the 'action point' of the animation we're
// now setting.
m_actor->GetModel()->SetAnimation( m_fsm_animation, true );
}
if( ( m_fsm_cyclepos <= action->m_Speed ) && ( nextpos > action->m_Speed ) )
{
DispatchEvent( contactEvent );
// Note that, at the moment, we don't care if the action succeeds or fails -
// we could check for failure, then abort the animation.
// It depends what we think is worse: stopping an animation halfway through,
// or playing the animation without getting a game effect.
// Could also check again here if the entity still exists, is in range, etc..
// and cancel if not, but we'll see how it looks without that, first.
}
if( nextpos >= ( action->m_Speed * 2 ) )
{
// End of cycle.
m_fsm_cyclepos = NOT_IN_CYCLE;
return( false );
}
// Otherwise, increment position.
m_fsm_cyclepos = nextpos;
return( false );
}
// Target's dead (or exhausted)? Then our work here is done.
if( !current->m_data[0].entity || !current->m_data[0].entity->m_extant )
@ -308,42 +344,13 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
return( false );
}
if( m_actor )
{
if( animation && ( m_actor->GetModel()->GetAnimation() == animation ) )
{
size_t cyclenext = cyclepos + timestep_millis;
if( ( cyclepos <= animation->m_ActionPos ) &&
( cyclenext > animation->m_ActionPos ) )
{
// Actually execute the action script in the sim frame
// that contains the animation's 'action point' (as
// specified by the artist that created it)
if( !DispatchEvent( contactEvent ) )
{
// The script is cancelling the attack/gather action.
// Cancel the animation (will probably cause a graphical
// glitch, but can't be helped)
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_WalkAnim );
}
}
cyclepos = cyclenext;
return( false );
}
// Just transitioned? No animation? (=> melee just finished) Play walk.
if( m_transition || !m_actor->GetModel()->GetAnimation() )
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_WalkAnim );
}
CVector2D delta = current->m_data[0].entity->m_position - m_position;
float adjRange = range + m_bounds->m_radius + current->m_data[0].entity->m_bounds->m_radius;
float adjRange = action->m_MaxRange + m_bounds->m_radius + current->m_data[0].entity->m_bounds->m_radius;
if( minRange > 0.0f )
if( action->m_MinRange > 0.0f )
{
float adjMinRange = m_meleeRangeMin + m_bounds->m_radius + current->m_data[0].entity->m_bounds->m_radius;
float adjMinRange = action->m_MinRange + m_bounds->m_radius + current->m_data[0].entity->m_bounds->m_radius;
if( delta.within( adjMinRange ) )
{
// Too close... do nothing.
@ -357,6 +364,14 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
// We're aiming to end up at a location just inside our maximum range
// (is this good enough?)
// Play walk for a bit.
if( m_actor && ( m_actor->GetModel()->GetAnimation() != m_actor->GetObject()->m_WalkAnim ) )
{
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_WalkAnim );
// Animation desync
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
}
delta = delta.normalize() * ( adjRange - m_bounds->m_radius );
current->m_data[0].location = (CVector2D)current->m_data[0].entity->m_position - delta;
@ -411,39 +426,52 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
}
else
{
// Close enough, but turn to face them.
// Close enough, but turn to face them.
m_orientation = atan2( delta.x, delta.y );
m_ahead = delta.normalize();
}
// Start the animation. Actual damage/gather will be done in a
// few hundred msec, at the 'action point' of the animation we're
// now setting.
// Pick our animation, calculate the time to play it, and start the timer.
m_fsm_animation = animation; // <- Replace with a call that gets one randomly, probably pass in a CSkeletonAnim* (void) fn for this purpose
// Here's the idea - we want to be at that animation's event point
// when the timer reaches action->m_Speed. The timer increments by 2 every millisecond.
// animation->m_actionpos is the time offset into that animation that event
// should happen. So...
m_fsm_anipos = action->m_Speed - ( m_fsm_animation->m_ActionPos * 2 );
// But...
if( action->m_Speed < ( m_fsm_animation->m_ActionPos * 2 ) )
{
// We ought to have started it in the past. Oh well.
// Here's what we'll do: play it now, and advance it to
// the point it should be by now.
m_actor->GetModel()->SetAnimation( m_fsm_animation, true );
m_actor->GetModel()->Update( m_fsm_animation->m_ActionPos / 1000.0f - action->m_Speed / 2000.0f );
}
m_actor->GetModel()->SetAnimation( animation, true );
cyclepos = 0;
m_fsm_cyclepos = 0;
return( false );
}
bool CEntity::processAttackMelee( CEntityOrder* current, size_t timestep_millis )
{
return( processContactAction( current, timestep_millis, CEntityOrder::ORDER_ATTACK_MELEE_NOPATHING, 0.5 ) );
return( processContactAction( current, timestep_millis, CEntityOrder::ORDER_ATTACK_MELEE_NOPATHING, &m_melee ) );
}
bool CEntity::processAttackMeleeNoPathing( CEntityOrder* current, size_t timestep_milli )
{
CEventAttack evt( current->m_data[0].entity );
return( processContactActionNoPathing( current, timestep_milli, m_actor ? m_actor->GetObject()->m_MeleeAnim : NULL, &evt, m_meleeRange, m_meleeRangeMin ) );
return( processContactActionNoPathing( current, timestep_milli, m_actor ? m_actor->GetObject()->m_MeleeAnim : NULL, &evt, &m_melee ) );
}
bool CEntity::processGather( CEntityOrder* current, size_t timestep_millis )
{
return( processContactAction( current, timestep_millis, CEntityOrder::ORDER_GATHER_NOPATHING, 0.5 ) );
return( processContactAction( current, timestep_millis, CEntityOrder::ORDER_GATHER_NOPATHING, &m_gather ) );
}
bool CEntity::processGatherNoPathing( CEntityOrder* current, size_t timestep_millis )
{
CEventGather evt( current->m_data[0].entity );
return( processContactActionNoPathing( current, timestep_millis, m_actor ? m_actor->GetObject()->m_GatherAnim : NULL, &evt, 5.0, 0.0 ) );
return( processContactActionNoPathing( current, timestep_millis, m_actor ? m_actor->GetObject()->m_GatherAnim : NULL, &evt, &m_gather ) );
}
bool CEntity::processGoto( CEntityOrder* current, size_t timestep_millis )

View File

@ -1,5 +1,8 @@
// Supporting data types for CEntity and related
#ifndef ENTITY_SUPPORT_INCLUDED
#define ENTITY_SUPPORT_INCLUDED
class CEntityManager;
class CDamageType : public CJSObject<CDamageType>
@ -68,3 +71,44 @@ public:
return( JS_TRUE );
}
};
struct SEntityAction
{
float m_MaxRange;
float m_MinRange;
size_t m_Speed;
SEntityAction() { m_MaxRange = m_MinRange = 0.0f; m_Speed = 1000; }
};
struct SClassSet
{
SClassSet* m_Parent;
STL_HASH_SET<CStrW, CStrW_hash_compare> m_Set;
std::vector<CStrW> m_Added;
std::vector<CStrW> m_Removed;
inline SClassSet() { m_Parent = NULL; }
inline bool IsMember( CStrW Test )
{ return( m_Set.find( Test ) != m_Set.end() ); }
inline void SetParent( SClassSet* Parent )
{ m_Parent = Parent; Rebuild(); }
void Rebuild()
{
if( m_Parent )
m_Set = m_Parent->m_Set;
else
m_Set.clear();
std::vector<CStrW>::iterator it;
for( it = m_Removed.begin(); it != m_Removed.end(); it++ )
m_Set.erase( *it );
for( it = m_Added.begin(); it != m_Added.end(); it++ )
m_Set.insert( *it );
}
};
#endif