diff --git a/binaries/data/mods/official/art/actors/units/celt_ijv_a.xml b/binaries/data/mods/official/art/actors/units/celt_ijv_a.xml
index ea17acbea7..2f779eb669 100644
--- a/binaries/data/mods/official/art/actors/units/celt_ijv_a.xml
+++ b/binaries/data/mods/official/art/actors/units/celt_ijv_a.xml
@@ -12,13 +12,14 @@
-
+
skeletal/m_pants_b.pmd
-
+
+
diff --git a/binaries/data/mods/official/art/actors/units/celt_ijv_b.xml b/binaries/data/mods/official/art/actors/units/celt_ijv_b.xml
index 8344bd5cc3..46dc825fab 100644
--- a/binaries/data/mods/official/art/actors/units/celt_ijv_b.xml
+++ b/binaries/data/mods/official/art/actors/units/celt_ijv_b.xml
@@ -12,13 +12,14 @@
-
+
skeletal/m_pants_b.pmd
-
+
+
skeletal/celt_ijv_b_01.dds
diff --git a/binaries/data/mods/official/art/actors/units/celt_ijv_e.xml b/binaries/data/mods/official/art/actors/units/celt_ijv_e.xml
index 7ed3835f55..13ebf4679d 100644
--- a/binaries/data/mods/official/art/actors/units/celt_ijv_e.xml
+++ b/binaries/data/mods/official/art/actors/units/celt_ijv_e.xml
@@ -12,14 +12,15 @@
-
+
skeletal/m_tights.pmd
-
+
+
skeletal/celt_ijv_e_01.dds
diff --git a/binaries/data/mods/official/art/actors/units/hele_iar_a.xml b/binaries/data/mods/official/art/actors/units/hele_iar_a.xml
index c3c95bf758..1511c77c36 100644
--- a/binaries/data/mods/official/art/actors/units/hele_iar_a.xml
+++ b/binaries/data/mods/official/art/actors/units/hele_iar_a.xml
@@ -12,7 +12,7 @@
-
+
skeletal/m_tunic_b.pmd
@@ -20,6 +20,8 @@
+
+
skeletal/hele_isp_b.dds
diff --git a/binaries/data/mods/official/art/actors/units/hele_iar_b.xml b/binaries/data/mods/official/art/actors/units/hele_iar_b.xml
index bd68f4fefb..b8af29fdd6 100644
--- a/binaries/data/mods/official/art/actors/units/hele_iar_b.xml
+++ b/binaries/data/mods/official/art/actors/units/hele_iar_b.xml
@@ -12,7 +12,7 @@
-
+
skeletal/m_tunic_b.pmd
@@ -20,6 +20,8 @@
+
+
skeletal/hele_iar_b.dds
diff --git a/binaries/data/mods/official/art/actors/units/hele_iar_e.xml b/binaries/data/mods/official/art/actors/units/hele_iar_e.xml
index b748d0b6e3..a3da8a05e5 100644
--- a/binaries/data/mods/official/art/actors/units/hele_iar_e.xml
+++ b/binaries/data/mods/official/art/actors/units/hele_iar_e.xml
@@ -12,7 +12,7 @@
-
+
skeletal/m_tunic_b.pmd
@@ -21,6 +21,8 @@
+
+
skeletal/plac_hele_e.dds
diff --git a/binaries/data/mods/official/entities/gaia/template_gaia_fauna.xml b/binaries/data/mods/official/entities/gaia/template_gaia_fauna.xml
index 7e22dd4d04..0d5651290e 100755
--- a/binaries/data/mods/official/entities/gaia/template_gaia_fauna.xml
+++ b/binaries/data/mods/official/entities/gaia/template_gaia_fauna.xml
@@ -21,5 +21,5 @@
subtype="meat"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/gaia/template_gaia_flora.xml b/binaries/data/mods/official/entities/gaia/template_gaia_flora.xml
index 0b28594f2e..dd496e4f3c 100755
--- a/binaries/data/mods/official/entities/gaia/template_gaia_flora.xml
+++ b/binaries/data/mods/official/entities/gaia/template_gaia_flora.xml
@@ -9,5 +9,5 @@
classes="gaia_flora"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/gaia/template_gaia_geo.xml b/binaries/data/mods/official/entities/gaia/template_gaia_geo.xml
index ef4004f402..15a61cf473 100755
--- a/binaries/data/mods/official/entities/gaia/template_gaia_geo.xml
+++ b/binaries/data/mods/official/entities/gaia/template_gaia_geo.xml
@@ -9,5 +9,5 @@
classes="gaia_geo"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/gaia/template_gaia_special.xml b/binaries/data/mods/official/entities/gaia/template_gaia_special.xml
index 05900383f8..10a79601b2 100755
--- a/binaries/data/mods/official/entities/gaia/template_gaia_special.xml
+++ b/binaries/data/mods/official/entities/gaia/template_gaia_special.xml
@@ -13,5 +13,5 @@
type="Special"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/gaia/template_tree.xml b/binaries/data/mods/official/entities/gaia/template_tree.xml
index 19b3b98819..a584347066 100755
--- a/binaries/data/mods/official/entities/gaia/template_tree.xml
+++ b/binaries/data/mods/official/entities/gaia/template_tree.xml
@@ -16,5 +16,5 @@
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_cc.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_cc.xml
index 1df4b0e8b8..cdeaaa0140 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_cc.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_cc.xml
@@ -31,5 +31,5 @@
construct="1"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_hc.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_hc.xml
index c5611cefb7..635fc63095 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_hc.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_hc.xml
@@ -19,5 +19,5 @@
add="5"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_ho.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_ho.xml
index 870b9fe138..a834c6fb7f 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_ho.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_ho.xml
@@ -19,5 +19,5 @@
add="5"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_tf.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_tf.xml
index 600be07806..7c169f4d1a 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_tf.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_tf.xml
@@ -20,5 +20,5 @@
max="60"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wc.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wc.xml
index 481d9265c4..d00e3aa395 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wc.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wc.xml
@@ -16,5 +16,5 @@
hitpoints="500"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wg.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wg.xml
index 92dc5d5e89..0739029062 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wg.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wg.xml
@@ -16,5 +16,5 @@
hitpoints="100"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wt.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wt.xml
index 33e19c6269..52f60a5a2a 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wt.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_civic_wt.xml
@@ -20,5 +20,5 @@
max="80"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_economic_fc.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_economic_fc.xml
index ebe0440c52..40be3990c0 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_economic_fc.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_economic_fc.xml
@@ -20,5 +20,5 @@
max="50"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_economic_rc.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_economic_rc.xml
index 18c8984e60..b6378f3211 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_economic_rc.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_economic_rc.xml
@@ -16,5 +16,5 @@
hitpoints="50"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_economic_tc.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_economic_tc.xml
index 3e7b354c95..f4eaa0f3f9 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_economic_tc.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_economic_tc.xml
@@ -23,5 +23,5 @@
add="10"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_military_ff.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_military_ff.xml
index 56661e1472..28cc75f7ec 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_military_ff.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_military_ff.xml
@@ -23,5 +23,5 @@
add="20"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_military_mc.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_military_mc.xml
index d84633c69f..a2d5fae8c8 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_military_mc.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_military_mc.xml
@@ -16,5 +16,5 @@
hitpoints="600"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_military_pc.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_military_pc.xml
index 9d9028b3a4..5f7cc1df26 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_military_pc.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_military_pc.xml
@@ -19,5 +19,5 @@
add="15"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_norm_special_sb.xml b/binaries/data/mods/official/entities/structures/template_structure_norm_special_sb.xml
index bc2c98c7ed..64d3d2b427 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_norm_special_sb.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_norm_special_sb.xml
@@ -12,5 +12,5 @@
classes="structure_city"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_resource_economic_fc.xml b/binaries/data/mods/official/entities/structures/template_structure_resource_economic_fc.xml
index ca221f21ed..df5b8dd4cd 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_resource_economic_fc.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_resource_economic_fc.xml
@@ -30,5 +30,5 @@
los="0"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/structures/template_structure_resource_economic_fv.xml b/binaries/data/mods/official/entities/structures/template_structure_resource_economic_fv.xml
index 1f3e87388e..919e1c3013 100755
--- a/binaries/data/mods/official/entities/structures/template_structure_resource_economic_fv.xml
+++ b/binaries/data/mods/official/entities/structures/template_structure_resource_economic_fv.xml
@@ -30,5 +30,5 @@
los="0"
/>
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/template_entity_script.js b/binaries/data/mods/official/entities/template_entity_script.js
index 6b8054bd5c..4d18ce7fa0 100644
--- a/binaries/data/mods/official/entities/template_entity_script.js
+++ b/binaries/data/mods/official/entities/template_entity_script.js
@@ -13,6 +13,79 @@ function entity_event_attack( evt )
// ====================================================================
+function entity_event_attack_ranged( evt )
+{
+ // Create a projectile from us, to the target, that will do some damage when it hits them.
+ dmg = new DamageType();
+ dmg.crush = parseInt(this.actions.attack.damage * this.actions.attack.crush);
+ dmg.hack = parseInt(this.actions.attack.damage * this.actions.attack.hack);
+ dmg.pierce = parseInt(this.actions.attack.damage * this.actions.attack.pierce);
+
+ // The parameters for Projectile are:
+ // 1 - The actor to use as the projectile. There are two ways of specifying this:
+ // the first is by giving an entity. The projectile's actor is found by looking
+ // in the actor of that entity. This way is usual, and preferred - visual
+ // information, like the projectile model, should be stored in the actor files.
+ // The second way is to give a actor/file name string (e.g. "props/weapon/weap_
+ // arrow_front.xml"). This is only meant to be used for 'Acts of Gaia' where
+ // there is no originating entity. Right now, this entity is the one doing the
+ // firing, so pass this.
+ // 2 - Where the projectile is coming from. This can be an entity or a Vector3D.
+ // For now, that's also us.
+ // 3 - Where the projectile is going to. Again, either a vector (attack ground?)
+ // or an entity. Let's shoot at our target, lest we get people terribly confused.
+ // 4 - How fast the projectile should go. To keep things clear, we'll set it to
+ // just a bit faster than the average cavalry.
+ // 5 - Who fired it? Erm... yep, us again.
+ // 6 - The function you'd like to call when it hits an entity.
+ // There's also a seventh parameter, for a function to call when it misses (more
+ // accurately, when it hits the floor). At the moment, however, the default
+ // action (do nothing) is what we want.
+ // Parameters 5, 6, and 7 are all optional.
+
+ projectile = new Projectile( this, this, evt.target, 12.0, this, projectile_event_impact )
+
+ // We'll attach the damage information to the projectile, just to show you can
+ // do that like you can with most other objects. Could also do this by making
+ // the function we pass a closure.
+
+ projectile.damage = dmg;
+
+ // Finally, tell the engine not to send this event to anywhere else -
+ // in particular, this shouldn't get to the melee event handler, above.
+
+ evt.stopPropagation();
+
+ console.write( "Fire!" );
+}
+
+// ====================================================================
+
+function projectile_event_impact( evt )
+{
+ console.write( "Hit!" );
+ evt.impacted.damage( this.damage, evt.originator );
+
+ // Just so you know - there's no guarantee that evt.impacted is the thing you were
+ // aiming at. This function gets called when the projectile hits *anything*.
+ // For example:
+
+ if( evt.impacted.player == evt.originator.player )
+ console.write( "Friendly fire!" );
+
+ // The three properties of the ProjectileImpact event are:
+ // - impacted, the thing it hit
+ // - originator, the thing that fired it (the fifth parameter of Projectile's
+ // constructor) - may be null
+ // - position, the position the arrow was in the world when it hit.
+
+ // The properties of the ProjectileMiss event (the one that gets sent to the
+ // handler that was the seventh parameter of the constructor) are similar,
+ // but it doesn't have 'impacted' - for obvious reasons.
+}
+
+// ====================================================================
+
function entity_event_gather( evt )
{
if (this.actions.gather[evt.target.traits.supply.type][evt.target.traits.supply.subtype])
diff --git a/binaries/data/mods/official/entities/units/template_unit.xml b/binaries/data/mods/official/entities/units/template_unit.xml
index 7226a7cf8e..97fc73b4fc 100755
--- a/binaries/data/mods/official/entities/units/template_unit.xml
+++ b/binaries/data/mods/official/entities/units/template_unit.xml
@@ -32,7 +32,7 @@
los="2"
/>
-
+
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/units/template_unit_infantry_ranged.xml b/binaries/data/mods/official/entities/units/template_unit_infantry_ranged.xml
index af713b72b4..d09d32cf05 100755
--- a/binaries/data/mods/official/entities/units/template_unit_infantry_ranged.xml
+++ b/binaries/data/mods/official/entities/units/template_unit_infantry_ranged.xml
@@ -10,4 +10,12 @@
classes="unit_ranged"
/>
+
+
+
+
+
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/gui/test/2_mainmenu.xml b/binaries/data/mods/official/gui/test/2_mainmenu.xml
index 95b6d85032..8fa87b2fc7 100644
--- a/binaries/data/mods/official/gui/test/2_mainmenu.xml
+++ b/binaries/data/mods/official/gui/test/2_mainmenu.xml
@@ -398,7 +398,7 @@
- gathertest
+ gathertest2
diff --git a/binaries/data/mods/official/maps/scenarios/gathertest2.pmp b/binaries/data/mods/official/maps/scenarios/gathertest2.pmp
new file mode 100644
index 0000000000..a37730c583
--- /dev/null
+++ b/binaries/data/mods/official/maps/scenarios/gathertest2.pmp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1143575ffdca143c02bddf9fea01ce44bcf15308a74901d695b388f240b26941
+size 741044
diff --git a/binaries/data/mods/official/maps/scenarios/gathertest2.xml b/binaries/data/mods/official/maps/scenarios/gathertest2.xml
new file mode 100644
index 0000000000..a519f783c2
--- /dev/null
+++ b/binaries/data/mods/official/maps/scenarios/gathertest2.xml
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:45d95a45122ab878be9472b468c2a3a87c0e95770cde4418c25fe535e8d9dd1d
+size 8201
diff --git a/binaries/data/profiles/default/settings/history b/binaries/data/profiles/default/settings/history
new file mode 100644
index 0000000000..d10cff4c7e
--- /dev/null
+++ b/binaries/data/profiles/default/settings/history
@@ -0,0 +1,50 @@
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 10.5, this.position.z ), target, 12 );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 10.5, this.position.z ), target, 12 );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 10.5, this.position.z ), target, 12 );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 10.5, this.position.z ), target, 12 );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 10.5, this.position.z ), target, 12 );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 10.5, this.position.z ), target, 12 );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 10.5, this.position.z ), target, 12 );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 10.5, this.position.z ), target, 12 );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 10.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 10, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?target = this
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?target = this;
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?new Projectile( "props/weapon/weap_arrow_front.xml", Vector3D( this.position.x, this.position.y + 2.5, this.position.z ), target, 12, this );
+?b
+?this.traits.health.curr
+?this.traits.health.max
+?entities.subset( function() { return( this.traits.health.cur < this.traits.health.max ); }
+?entities.subset( function() { return( this.traits.health.cur < this.traits.health.max ); } );
+?entities.subset( function() { return( this.traits.health.curr < this.traits.health.max ); } );
+?selection = entities.subset( function() { return( this.traits.health.curr < this.traits.health.max ); } );
diff --git a/binaries/data/profiles/default/settings/user.cfg b/binaries/data/profiles/default/settings/user.cfg
new file mode 100644
index 0000000000..b388d1a42f
--- /dev/null
+++ b/binaries/data/profiles/default/settings/user.cfg
@@ -0,0 +1,3 @@
+; Settings in a profile config are used instead of the equivalent settings in the system.cfg file,
+; but note that not some settings are only allowed to be specified in system.cfg, for security reasons.
+
diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp
index bc2c5f1c2a..b89e44e09c 100755
--- a/source/graphics/GameView.cpp
+++ b/source/graphics/GameView.cpp
@@ -20,10 +20,10 @@
#include "Profile.h"
#include "LoaderThunks.h"
-
#include "Quaternion.h"
#include "Unit.h"
#include "Model.h"
+#include "Projectile.h"
#include "sdl.h"
#include "input.h"
@@ -129,7 +129,7 @@ void CGameView::Render()
PROFILE_END( "render terrain" );
MICROLOG(L"render models");
PROFILE_START( "render models" );
- RenderModels(m_pWorld->GetUnitManager());
+ RenderModels(m_pWorld->GetUnitManager(), m_pWorld->GetProjectileManager());
PROFILE_END( "render models" );
}
@@ -147,7 +147,7 @@ void CGameView::RenderTerrain(CTerrain *pTerrain)
}
}
-void CGameView::RenderModels(CUnitManager *pUnitMan)
+void CGameView::RenderModels(CUnitManager *pUnitMan, CProjectileManager *pProjectileMan)
{
CFrustum frustum=m_Camera.GetFrustum();
@@ -157,6 +157,13 @@ void CGameView::RenderModels(CUnitManager *pUnitMan)
SubmitModelRecursive(units[i]->GetModel());
}
}
+
+ const std::vector& projectiles=pProjectileMan->GetProjectiles();
+ for (uint i=0;iGetModel()->GetBounds())) {
+ SubmitModelRecursive(projectiles[i]->GetModel());
+ }
+ }
}
void CGameView::SubmitModelRecursive(CModel* model)
diff --git a/source/graphics/GameView.h b/source/graphics/GameView.h
index b926765422..5d261db69c 100755
--- a/source/graphics/GameView.h
+++ b/source/graphics/GameView.h
@@ -9,6 +9,7 @@ class CGameAttributes;
class CWorld;
class CTerrain;
class CUnitManager;
+class CProjectileManager;
class CModel;
class CGameView
@@ -43,7 +44,7 @@ class CGameView
// RenderModels: iterate through model list and submit all models in viewing
// frustum to the Renderer
- void RenderModels(CUnitManager *pUnitMan);
+ void RenderModels(CUnitManager *pUnitMan, CProjectileManager *pProjectileManager);
// SubmitModelRecursive: recurse down given model, submitting it and all its
// descendents to the renderer
diff --git a/source/graphics/Model.cpp b/source/graphics/Model.cpp
index 33c5017ff8..5bbb7cabc6 100755
--- a/source/graphics/Model.cpp
+++ b/source/graphics/Model.cpp
@@ -213,8 +213,8 @@ CSkeletonAnim* CModel::BuildAnimation(const char* filename,float speed,double ac
CSkeletonAnim* anim=new CSkeletonAnim;
anim->m_AnimDef=def;
anim->m_Speed=speed;
- anim->m_ActionPos=(size_t)( actionpos * anim->m_AnimDef->GetDuration() );
- anim->m_ActionPos2=(size_t)( actionpos2 * anim->m_AnimDef->GetDuration() );
+ anim->m_ActionPos=(size_t)( actionpos * anim->m_AnimDef->GetDuration() / speed );
+ anim->m_ActionPos2=(size_t)( actionpos2 * anim->m_AnimDef->GetDuration() / speed );
anim->m_ObjectBounds.SetEmpty();
InvalidateBounds();
@@ -361,6 +361,7 @@ void CModel::RemoveProp(SPropPoint* point)
for (Iter iter=m_Props.begin();iter!=m_Props.end();++iter) {
const Prop& prop=*iter;
if (prop.m_Point==point) {
+ delete prop.m_Model;
m_Props.erase(iter);
return;
}
diff --git a/source/graphics/ObjectEntry.cpp b/source/graphics/ObjectEntry.cpp
index bebbb3bd0d..cc244e19dd 100755
--- a/source/graphics/ObjectEntry.cpp
+++ b/source/graphics/ObjectEntry.cpp
@@ -30,6 +30,9 @@ CObjectEntry::CObjectEntry(int type, CObjectBase* base)
m_MeleeAnim=0;
m_GatherAnim=0;
m_RangedAnim=0;
+ m_ProjectileModel=0;
+ m_AmmunitionPoint=0;
+ m_AmmunitionModel=0;
}
CObjectEntry::~CObjectEntry()
@@ -189,11 +192,32 @@ bool CObjectEntry::BuildRandomVariant(CObjectBase::variation_key& vars, CObjectB
for (size_t p = 0; p < m_Props.size(); p++)
{
const CObjectBase::Prop& prop = m_Props[p];
- SPropPoint* proppoint = modeldef->FindPropPoint((const char*) prop.m_PropPointName);
- if (proppoint)
+
+ CObjectEntry* oe = g_ObjMan.FindObjectVariation(prop.m_ModelName, vars, vars_it);
+ if (!oe)
{
- CObjectEntry* oe = g_ObjMan.FindObjectVariation(prop.m_ModelName, vars, vars_it);
- if (oe)
+ LOG(ERROR, LOG_CATEGORY, "Failed to build prop model \"%s\" on actor \"%s\"", (const char*)prop.m_ModelName, (const char*)m_Base->m_ShortName);
+ continue;
+ }
+
+ // Pluck out the special attachpoint 'projectile'
+ if( prop.m_PropPointName == "projectile" )
+ {
+ m_ProjectileModel = oe->m_Model;
+ }
+ // Also the other special attachpoint 'loaded-'
+ else if( ( prop.m_PropPointName.Length() > 7 ) && ( prop.m_PropPointName.Left( 7 ) == "loaded-" ) )
+ {
+ CStr ppn = prop.m_PropPointName.GetSubstring( 7, prop.m_PropPointName.Length() - 7 );
+ m_AmmunitionModel = oe->m_Model;
+ m_AmmunitionPoint = modeldef->FindPropPoint((const char*)ppn );
+ if( !m_AmmunitionPoint )
+ LOG(ERROR, LOG_CATEGORY, "Failed to find matching prop point called \"%s\" in model \"%s\" on actor \"%s\"", (const char*)ppn, modelfilename, (const char*)prop.m_ModelName);
+ }
+ else
+ {
+ SPropPoint* proppoint = modeldef->FindPropPoint((const char*) prop.m_PropPointName);
+ if (proppoint)
{
CModel* propmodel = oe->m_Model->Clone();
m_Model->AddProp(proppoint, propmodel);
@@ -201,13 +225,7 @@ bool CObjectEntry::BuildRandomVariant(CObjectBase::variation_key& vars, CObjectB
propmodel->SetAnimation(oe->m_IdleAnim);
}
else
- {
- LOG(ERROR, LOG_CATEGORY, "Failed to build prop model \"%s\" on actor \"%s\"", (const char*)prop.m_ModelName, (const char*)m_Base->m_ShortName);
- }
- }
- else
- {
- LOG(ERROR, LOG_CATEGORY, "Failed to find matching prop point called \"%s\" in model \"%s\" on actor \"%s\"", (const char*)prop.m_PropPointName, modelfilename, (const char*)prop.m_ModelName);
+ LOG(ERROR, LOG_CATEGORY, "Failed to find matching prop point called \"%s\" in model \"%s\" on actor \"%s\"", (const char*)prop.m_PropPointName, modelfilename, (const char*)prop.m_ModelName);
}
}
diff --git a/source/graphics/ObjectEntry.h b/source/graphics/ObjectEntry.h
index dccf5ff8b2..b8a19f6104 100755
--- a/source/graphics/ObjectEntry.h
+++ b/source/graphics/ObjectEntry.h
@@ -3,6 +3,7 @@
class CModel;
class CSkeletonAnim;
+struct SPropPoint;
#include
#include "CStr.h"
@@ -39,6 +40,10 @@ public:
CSkeletonAnim* m_RangedAnim;
CSkeletonAnim* m_CorpseAnim;
+ CModel* m_ProjectileModel;
+ CModel* m_AmmunitionModel;
+ SPropPoint* m_AmmunitionPoint;
+
CSkeletonAnim* GetNamedAnimation( CStr animationName );
// list of props attached to object
std::vector m_Props;
diff --git a/source/graphics/Unit.cpp b/source/graphics/Unit.cpp
index 4926703296..5c2786857b 100644
--- a/source/graphics/Unit.cpp
+++ b/source/graphics/Unit.cpp
@@ -2,7 +2,32 @@
#include "Unit.h"
#include "Model.h"
+#include "ObjectEntry.h"
CUnit::~CUnit() {
delete m_Model;
}
+
+void CUnit::ShowAmmunition()
+{
+ if( !m_Object->m_AmmunitionModel || !m_Object->m_AmmunitionPoint )
+ return;
+ m_Model->AddProp( m_Object->m_AmmunitionPoint, m_Object->m_AmmunitionModel->Clone() );
+}
+
+void CUnit::HideAmmunition()
+{
+ if( !m_Object->m_AmmunitionModel || !m_Object->m_AmmunitionPoint )
+ return;
+ // Find out what the usual prop is:
+ std::vector& props = m_Object->m_Model->GetProps();
+ std::vector::iterator it;
+ for( it = props.begin(); it != props.end(); ++it )
+ if( it->m_Point == m_Object->m_AmmunitionPoint )
+ {
+ m_Model->AddProp( m_Object->m_AmmunitionPoint, it->m_Model->Clone() );
+ return;
+ }
+ // No usual prop.
+ m_Model->RemoveProp( m_Object->m_AmmunitionPoint );
+}
\ No newline at end of file
diff --git a/source/graphics/Unit.h b/source/graphics/Unit.h
index f8df0169cf..36cecb25f2 100755
--- a/source/graphics/Unit.h
+++ b/source/graphics/Unit.h
@@ -31,6 +31,10 @@ public:
// get actor's entity
CEntity* GetEntity() { return m_Entity; }
+ // Put here as it conveniently references both the model and the ObjectEntry
+ void ShowAmmunition();
+ void HideAmmunition();
+
private:
// object from which unit was created
CObjectEntry* m_Object;
diff --git a/source/graphics/UnitManager.cpp b/source/graphics/UnitManager.cpp
index fe2c591e07..053d0d5f15 100755
--- a/source/graphics/UnitManager.cpp
+++ b/source/graphics/UnitManager.cpp
@@ -65,12 +65,14 @@ void CUnitManager::DeleteUnit(CUnit* unit)
// DeleteAll: remove and delete all units
void CUnitManager::DeleteAll()
{
- for (uint i=0;im_Model->Clone(), entity);
AddUnit(unit);
return unit;
-}
+}
\ No newline at end of file
diff --git a/source/graphics/UnitManager.h b/source/graphics/UnitManager.h
index 5785ea092b..35f2e8480e 100755
--- a/source/graphics/UnitManager.h
+++ b/source/graphics/UnitManager.h
@@ -13,6 +13,7 @@
#include "Singleton.h"
class CUnit;
+class CModel;
class CVector3D;
class CEntity;
class CStr;
diff --git a/source/main.cpp b/source/main.cpp
index 4d88d4b524..bf0884e64a 100755
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -55,6 +55,7 @@
#include "EntityManager.h"
#include "PathfindEngine.h"
#include "Scheduler.h"
+#include "Projectile.h"
#include "StringConvert.h"
#include "scripting/ScriptingHost.h"
@@ -635,9 +636,14 @@ static void Render()
static void LoadProfile( CStr profile )
{
- CStr filename = CStr( "profiles/" ) + profile + CStr( ".cfg" );
- g_ConfigDB.SetConfigFile( CFG_USER, true, filename );
+ CStr base = CStr( "profiles/" ) + profile;
+ g_ConfigDB.SetConfigFile( CFG_USER, true, base + "/settings/user.cfg" );
g_ConfigDB.Reload( CFG_USER );
+ int history_size = 50;
+ CConfigValue* config_value = g_ConfigDB.GetValue( CFG_USER, "console.history.size" );
+ if( config_value )
+ config_value->GetInt( history_size );
+ g_Console->UseHistoryFile( base + "/settings/history", ( history_size >= 0 ) ? history_size : 50 );
}
// Fill in the globals from the config files.
@@ -793,6 +799,7 @@ TIMER(InitScripting)
CJSComplexPropertyAccessor::ScriptingInit(); // <-- Doesn't really matter which we use, but we know CJSPropertyAccessor is already being compiled for T = CEntity.
CScriptEvent::ScriptingInit();
CJSProgressTimer::ScriptingInit();
+ CProjectile::ScriptingInit();
g_ScriptingHost.DefineConstant( "ORDER_NONE", -1 );
g_ScriptingHost.DefineConstant( "ORDER_GOTO", CEntityOrder::ORDER_GOTO );
@@ -831,7 +838,7 @@ TIMER(InitVfs)
vfs_init();
vfs_mount("", "mods/official", VFS_MOUNT_RECURSIVE|VFS_MOUNT_ARCHIVES|VFS_MOUNT_WATCH);
vfs_mount("screenshots/", "screenshots");
- vfs_mount("profiles/", "profiles");
+ vfs_mount("profiles/", "profiles", VFS_MOUNT_RECURSIVE);
}
/*
double t0 = get_time();
diff --git a/source/ps/CConsole.cpp b/source/ps/CConsole.cpp
index 84a3b25148..a40282f317 100755
--- a/source/ps/CConsole.cpp
+++ b/source/ps/CConsole.cpp
@@ -13,6 +13,8 @@
#include "Network/Client.h"
#include "Network/Server.h"
+#include "lib/res/vfs.h"
+
#include "Interact.h"
extern bool keys[SDLK_LAST];
@@ -460,6 +462,12 @@ void CConsole::SetBuffer(const wchar_t* szMessage, ...)
m_iBufferLength = m_iBufferPos = (int)wcslen(m_szBuffer);
}
+void CConsole::UseHistoryFile( CStr filename, unsigned int historysize )
+{
+ m_sHistoryFile = filename;
+ m_iHistorySize = historysize;
+ LoadHistory();
+}
void CConsole::ProcessBuffer(const wchar_t* szLine){
if (szLine == NULL) return;
@@ -468,6 +476,8 @@ void CConsole::ProcessBuffer(const wchar_t* szLine){
assert(wcslen(szLine) < CONSOLE_BUFFER_SIZE);
m_deqBufHistory.push_front(szLine);
+ SaveHistory(); // Do this each line for the moment; if a script causes
+ // a crash it's a useful record.
wchar_t szCommand[CONSOLE_BUFFER_SIZE];
memset(szCommand, '\0', sizeof(wchar_t) * CONSOLE_BUFFER_SIZE);
@@ -545,6 +555,44 @@ void CConsole::ProcessBuffer(const wchar_t* szLine){
SendChatMessage(szLine);
}
+void CConsole::LoadHistory()
+{
+ void* buffer; unsigned int buflen;
+ Handle h = vfs_load( m_sHistoryFile, buffer, buflen );
+ if( h > 0 )
+ {
+ std::string utf8( (char*)buffer, buflen );
+ CStrW str( utf8 );
+ size_t pos = 0;
+ while( pos != -1 )
+ {
+ pos = str.find( '\n' );
+ if( pos != -1 )
+ {
+ if( pos > 0 )
+ m_deqBufHistory.push_front( str.Left( str[pos-1] == '\r' ? pos - 1 : pos ) );
+ str = str.GetSubstring( pos + 1, str.npos );
+ }
+ else if( str.Length() > 0 )
+ m_deqBufHistory.push_front( str );
+ }
+ }
+ // Don't care about failure; just don't load anything.
+}
+
+void CConsole::SaveHistory()
+{
+ CStr buffer;
+ std::deque::iterator it;
+ unsigned int count = 0;
+ for( it = m_deqBufHistory.begin(); it != m_deqBufHistory.end(); ++it )
+ {
+ if( count++ >= m_iHistorySize ) break;
+ buffer = CStr( *it ) + "\n" + buffer;
+ }
+ vfs_store( m_sHistoryFile, (void*)buffer.c_str(), buffer.Length(), FILE_NO_AIO );
+}
+
void CConsole::SendChatMessage(const wchar_t *szMessage)
{
if (g_NetClient || g_NetServer)
diff --git a/source/ps/CConsole.h b/source/ps/CConsole.h
index 5287c32d3e..486a30be34 100755
--- a/source/ps/CConsole.h
+++ b/source/ps/CConsole.h
@@ -19,7 +19,7 @@
#include "ogl.h"
#include "lib.h"
#include "sdl.h"
-
+#include "CStr.h"
#ifndef CCONSOLE_H
#define CCONSOLE_H
@@ -53,6 +53,9 @@ private:
int m_iBufferPos;
int m_iBufferLength;
+ CStr m_sHistoryFile;
+ unsigned int m_iHistorySize;
+
bool m_bFocus;
bool m_bVisible; // console is to be drawn
bool m_bToggle; // show/hide animation is currently active
@@ -73,6 +76,9 @@ private:
void InsertBuffer(void){InsertMessage(L"%ls", m_szBuffer);};
void ProcessBuffer(const wchar_t* szLine);
+ void LoadHistory();
+ void SaveHistory();
+
public:
CConsole();
~CConsole();
@@ -94,6 +100,8 @@ public:
void SetBuffer(const wchar_t* szMessage, ...);
+ void UseHistoryFile( CStr filename, unsigned int historysize );
+
// Only returns a pointer to the buffer; copy out of here if you want to keep it.
const wchar_t* GetBuffer();
void FlushBuffer();
diff --git a/source/ps/World.cpp b/source/ps/World.cpp
index 18bef89379..98d9645eae 100755
--- a/source/ps/World.cpp
+++ b/source/ps/World.cpp
@@ -60,4 +60,5 @@ CWorld::~CWorld()
// The reason for not keeping the instance around is that we require a
// clean slate for each game start.
delete &m_EntityManager;
+ delete &m_ProjectileManager;
}
diff --git a/source/ps/World.h b/source/ps/World.h
index 9bbc4b28da..9837ab631b 100755
--- a/source/ps/World.h
+++ b/source/ps/World.h
@@ -4,6 +4,7 @@
#include "Terrain.h"
#include "UnitManager.h"
#include "EntityManager.h"
+#include "Projectile.h"
class CGame;
class CGameAttributes;
@@ -13,18 +14,20 @@ class CWorld
CGame *m_pGame;
CTerrain m_Terrain;
- // These both point to the respective g_* globals - the plan is to remove
+ // These all point to the respective g_* globals - the plan is to remove
// the globals and move them into CWorld members as soon as all code has
// been converted
CUnitManager &m_UnitManager;
CEntityManager &m_EntityManager;
+ CProjectileManager &m_ProjectileManager;
public:
inline CWorld(CGame *pGame):
m_pGame(pGame),
m_Terrain(),
m_UnitManager(g_UnitMan),
- m_EntityManager(*(new CEntityManager()))
+ m_EntityManager(*(new CEntityManager())),
+ m_ProjectileManager( *(new CProjectileManager()))
{}
~CWorld();
@@ -40,6 +43,8 @@ public:
{ return &m_Terrain; }
inline CUnitManager *GetUnitManager()
{ return &m_UnitManager; }
+ inline CProjectileManager *GetProjectileManager()
+ { return &m_ProjectileManager; }
};
#include "Game.h"
diff --git a/source/scripting/DOMEvent.cpp b/source/scripting/DOMEvent.cpp
index de39a3d88e..c0c3a1573d 100755
--- a/source/scripting/DOMEvent.cpp
+++ b/source/scripting/DOMEvent.cpp
@@ -13,6 +13,8 @@ IEventTarget::~IEventTarget()
bool IEventTarget::_DispatchEvent( CScriptEvent* evt, IEventTarget* target )
{
+ // TODO: Deal correctly with multiple handlers
+
if( before && before->_DispatchEvent( evt, target ) )
return( true ); // Stop propagation.
diff --git a/source/scripting/EventTypes.h b/source/scripting/EventTypes.h
index 1c9cfd7507..452d3a4944 100644
--- a/source/scripting/EventTypes.h
+++ b/source/scripting/EventTypes.h
@@ -13,6 +13,9 @@ enum EEventType
EVENT_PREPARE_ORDER,
EVENT_ORDER_TRANSITION,
EVENT_LAST,
+ // Projectile events
+ EVENT_IMPACT = 0,
+ EVENT_MISS,
// General events
EVENT_GAME_START = 0,
EVENT_GAME_TICK,
diff --git a/source/scripting/JSConversions.cpp b/source/scripting/JSConversions.cpp
index 8ae2ab5e53..06c024c865 100755
--- a/source/scripting/JSConversions.cpp
+++ b/source/scripting/JSConversions.cpp
@@ -11,7 +11,7 @@
template<> HEntity* ToNative( JSContext* cx, JSObject* obj )
{
CEntity* e = ToNative( cx, obj );
- return( &( e->me ) );
+ return( e ? &( e->me ) : NULL );
}
template<> JSObject* ToScript( HEntity* Native )
@@ -22,7 +22,7 @@ template<> JSObject* ToScript( HEntity* Native )
// CPlayer*
template<> bool ToPrimitive( JSContext* cx, jsval v, CPlayer*& Storage )
{
- if( !JSVAL_IS_OBJECT( v ) ) return( false );
+ if( !JSVAL_IS_OBJECT( v ) || ( v == JSVAL_NULL ) ) return( false );
CPlayer* Data = (CPlayer*)JS_GetInstancePrivate( cx, JSVAL_TO_OBJECT( v ), &CPlayer::JSI_class, NULL );
if( !Data ) return( false );
Storage = Data;
@@ -38,7 +38,7 @@ template<> JSObject* ToScript( CPlayer** Native )
template<> bool ToPrimitive( JSContext* cx, jsval v, CBaseEntity*& Storage )
{
- if( !JSVAL_IS_OBJECT( v ) ) return( false );
+ if( !JSVAL_IS_OBJECT( v ) || ( v == JSVAL_NULL ) ) return( false );
CBaseEntity* Data = (CBaseEntity*)JS_GetInstancePrivate( cx, JSVAL_TO_OBJECT( v ), &CBaseEntity::JSI_class, NULL );
if( !Data ) return( false );
Storage = Data;
@@ -54,8 +54,8 @@ template<> JSObject* ToScript( CBaseEntity** Native )
template<> CVector3D* ToNative( JSContext* cx, JSObject* obj )
{
- JSI_Vector3D::Vector3D_Info* v = (JSI_Vector3D::Vector3D_Info*)JS_GetPrivate( cx, obj );
- return( v->vector );
+ JSI_Vector3D::Vector3D_Info* v = (JSI_Vector3D::Vector3D_Info*)JS_GetInstancePrivate( cx, obj, &JSI_Vector3D::JSI_class, NULL );
+ return( v ? v->vector : NULL );
}
template<> JSObject* ToScript( CVector3D* Native )
diff --git a/source/scripting/JSConversions.h b/source/scripting/JSConversions.h
index 43247968cc..c2fbd47096 100755
--- a/source/scripting/JSConversions.h
+++ b/source/scripting/JSConversions.h
@@ -23,25 +23,20 @@ class CPlayer;
template T* ToNative( JSContext* cx, JSObject* obj )
{
-#ifndef NDEBUG
- if( OBJECT_TO_JSVAL( obj ) == JSVAL_NULL )
- return( NULL );
- assert( JS_GetClass( obj ) == &T::JSI_class );
- return( (T*)JS_GetPrivate( cx, obj ) );
-#endif
return( (T*)JS_GetInstancePrivate( cx, obj, &T::JSI_class, NULL ) );
}
template JSObject* ToScript( T* Native )
{
if( !Native )
- return( (JSObject*)JSVAL_NULL );
+ return( (JSObject*)NULL );
return( Native->GetScript() );
}
template T* ToNative( jsval v )
{
if( !JSVAL_IS_OBJECT( v ) ) return( NULL );
+ if( v == JSVAL_NULL ) return( NULL );
return( ToNative( g_ScriptingHost.GetContext(), JSVAL_TO_OBJECT( v ) ) );
}
diff --git a/source/scripting/ScriptableObject.h b/source/scripting/ScriptableObject.h
index b8304aac91..9f774303c7 100755
--- a/source/scripting/ScriptableObject.h
+++ b/source/scripting/ScriptableObject.h
@@ -13,6 +13,8 @@
#define ALLOW_NONSHARED_NATIVES
+static int pcount = 0;
+
class IJSObject;
class IJSProperty
@@ -137,7 +139,7 @@ public:
void Root()
{
if( JSVAL_IS_GCTHING( m_Data ) )
- JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_Data );
+ JS_AddNamedRoot( g_ScriptingHost.GetContext(), (void*)&m_Data, "ScriptableObjectProperty" );
}
void Uproot()
{
@@ -337,7 +339,7 @@ public:
{
m_JS = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_class, NULL, NULL );
if( m_EngineOwned )
- JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_JS );
+ JS_AddNamedRoot( g_ScriptingHost.GetContext(), (void*)&m_JS, JSI_class.name );
JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, (T*)this );
}
diff --git a/source/simulation/BaseEntity.cpp b/source/simulation/BaseEntity.cpp
index 2dc69fda0b..9741edc6cc 100755
--- a/source/simulation/BaseEntity.cpp
+++ b/source/simulation/BaseEntity.cpp
@@ -59,11 +59,13 @@ void CBaseEntity::loadBase()
{
m_bound_circle = new CBoundingCircle();
m_bound_circle->setRadius( m_base->m_bound_circle->m_radius );
+ m_bound_circle->setHeight( m_base->m_bound_circle->m_height );
}
else if( m_base->m_bound_type == CBoundingObject::BOUND_OABB )
{
m_bound_box = new CBoundingBox();
- m_bound_box->setDimensions( m_base->m_bound_box->getWidth(), m_base->m_bound_box->getHeight() );
+ m_bound_box->setDimensions( m_base->m_bound_box->getWidth(), m_base->m_bound_box->getDepth() );
+ m_bound_box->setHeight( m_base->m_bound_box->m_height );
}
m_bound_type = m_base->m_bound_type;
}
@@ -157,6 +159,7 @@ bool CBaseEntity::loadXML( CStr filename )
AT(parent);
AT(radius);
AT(width);
+ AT(depth);
AT(height);
AT(on);
AT(file);
@@ -217,17 +220,22 @@ bool CBaseEntity::loadXML( CStr filename )
if( !m_bound_circle )
m_bound_circle = new CBoundingCircle();
CStrW radius (Child.getAttributes().getNamedItem(at_radius));
+ CStrW height (Child.getAttributes().getNamedItem(at_height));
+
m_bound_circle->setRadius( radius.ToFloat() );
+ m_bound_circle->setHeight( height.ToFloat() );
m_bound_type = CBoundingObject::BOUND_CIRCLE;
}
else
{
if( !m_bound_box )
- m_bound_box = new CBoundingBox();
+ m_bound_box = new CBoundingBox();
CStrW width (Child.getAttributes().getNamedItem(at_width));
+ CStrW depth (Child.getAttributes().getNamedItem(at_depth));
CStrW height (Child.getAttributes().getNamedItem(at_height));
- m_bound_box->setDimensions( width.ToFloat(), height.ToFloat() );
+ m_bound_box->setDimensions( width.ToFloat(), depth.ToFloat() );
+ m_bound_box->setHeight( height.ToFloat() );
m_bound_type = CBoundingObject::BOUND_OABB;
}
}
diff --git a/source/simulation/BoundingObjects.cpp b/source/simulation/BoundingObjects.cpp
index c1167d69ee..bab387eca3 100755
--- a/source/simulation/BoundingObjects.cpp
+++ b/source/simulation/BoundingObjects.cpp
@@ -30,11 +30,23 @@ bool CBoundingObject::contains( const CVector2D& point )
return( _contains( point, delta ) );
}
-CBoundingCircle::CBoundingCircle( float x, float y, float radius )
+
+void CBoundingObject::setPosition( float x, float y )
+{
+ m_pos.x = x; m_pos.y = y;
+}
+
+void CBoundingObject::setHeight( float height )
+{
+ m_height = height;
+}
+
+CBoundingCircle::CBoundingCircle( float x, float y, float radius, float height )
{
m_type = BOUND_CIRCLE;
setPosition( x, y );
setRadius( radius );
+ setHeight( height );
}
CBoundingCircle::CBoundingCircle( float x, float y, CBoundingCircle* copy )
@@ -42,11 +54,7 @@ CBoundingCircle::CBoundingCircle( float x, float y, CBoundingCircle* copy )
m_type = BOUND_CIRCLE;
setPosition( x, y );
setRadius( copy->m_radius );
-}
-
-void CBoundingObject::setPosition( float x, float y )
-{
- m_pos.x = x; m_pos.y = y;
+ setHeight( copy->m_height );
}
void CBoundingCircle::setRadius( float radius )
@@ -82,11 +90,12 @@ void CBoundingCircle::render( float height )
glEnd();
}
-CBoundingBox::CBoundingBox( float x, float y, const CVector2D& u, float width, float height )
+CBoundingBox::CBoundingBox( float x, float y, const CVector2D& u, float width, float depth, float height )
{
m_type = BOUND_OABB;
setPosition( x, y );
- setDimensions( width, height );
+ setDimensions( width, depth );
+ setHeight( height );
setOrientation( u );
}
@@ -94,15 +103,17 @@ CBoundingBox::CBoundingBox( float x, float y, const CVector2D& u, CBoundingBox*
{
m_type = BOUND_OABB;
setPosition( x, y );
- setDimensions( copy->getWidth(), copy->getHeight() );
+ setDimensions( copy->getWidth(), copy->getDepth() );
+ setHeight( copy->m_height );
setOrientation( u );
}
-CBoundingBox::CBoundingBox( float x, float y, float orientation, float width, float height )
+CBoundingBox::CBoundingBox( float x, float y, float orientation, float width, float depth, float height )
{
m_type = BOUND_OABB;
setPosition( x, y );
- setDimensions( width, height );
+ setDimensions( width, depth );
+ setHeight( height );
setOrientation( orientation );
}
@@ -110,15 +121,16 @@ CBoundingBox::CBoundingBox( float x, float y, float orientation, CBoundingBox* c
{
m_type = BOUND_OABB;
setPosition( x, y );
- setDimensions( copy->getWidth(), copy->getHeight() );
+ setDimensions( copy->getWidth(), copy->getDepth() );
+ setHeight( copy->m_height );
setOrientation( orientation );
}
-void CBoundingBox::setDimensions( float width, float height )
+void CBoundingBox::setDimensions( float width, float depth )
{
m_w = width / 2.0f;
- m_h = height / 2.0f;
- m_radius = sqrt( ( m_w * m_w ) + ( m_h * m_h ) );
+ m_d = depth / 2.0f;
+ m_radius = sqrt( ( m_w * m_w ) + ( m_d * m_d ) );
}
void CBoundingBox::setOrientation( float orientation )
@@ -143,8 +155,8 @@ bool CBoundingBox::_intersects( CBoundingObject* obj, const CVector2D& delta )
// Imperfect but quick...
CBoundingCircle* c = (CBoundingCircle*)obj;
- float deltah = fabs( delta.dot( m_u ) );
- if( deltah > ( m_h + c->m_radius ) ) return( false );
+ float deltad = fabs( delta.dot( m_u ) );
+ if( deltad > ( m_d + c->m_radius ) ) return( false );
float deltaw = fabs( delta.dot( m_v ) );
if( deltaw > ( m_w + c->m_radius ) ) return( false );
return( true );
@@ -180,8 +192,8 @@ bool CBoundingBox::_intersects( CBoundingObject* obj, const CVector2D& delta )
// Project box 2 onto v-axis of box 1
- prj1 = fabs( vu * b->m_h + vv * b->m_w );
- prj2 = fabs( vu * b->m_h - vv * b->m_h );
+ prj1 = fabs( vu * b->m_d + vv * b->m_w );
+ prj2 = fabs( vu * b->m_d - vv * b->m_w );
dm = delta.dot( m_v );
if( prj1 > prj2 )
@@ -193,21 +205,21 @@ bool CBoundingBox::_intersects( CBoundingObject* obj, const CVector2D& delta )
// Project box 2 onto u-axis of box 1
- prj1 = fabs( uu * b->m_h + uv * b->m_w );
- prj2 = fabs( uu * b->m_h - uv * b->m_w );
+ prj1 = fabs( uu * b->m_d + uv * b->m_w );
+ prj2 = fabs( uu * b->m_d - uv * b->m_w );
dm = delta.dot( m_u );
if( prj1 > prj2 )
{
- if( ( dm - prj1 ) > m_h ) return( false );
+ if( ( dm - prj1 ) > m_d ) return( false );
}
else
- if( ( dm - prj2 ) > m_h ) return( false );
+ if( ( dm - prj2 ) > m_d ) return( false );
// Project box 1 onto v-axis of box 2
- prj1 = fabs( uv * m_h + vv * m_w );
- prj2 = fabs( uv * m_h - vv * m_w );
+ prj1 = fabs( uv * m_d + vv * m_w );
+ prj2 = fabs( uv * m_d - vv * m_w );
dm = delta.dot( b->m_v );
if( prj1 > prj2 )
@@ -219,16 +231,16 @@ bool CBoundingBox::_intersects( CBoundingObject* obj, const CVector2D& delta )
// Project box 1 onto u-axis of box 2
- prj1 = fabs( uu * m_h + vu * m_w );
- prj2 = fabs( uu * m_h - vu * m_w );
+ prj1 = fabs( uu * m_d + vu * m_w );
+ prj2 = fabs( uu * m_d - vu * m_w );
dm = delta.dot( b->m_u );
if( prj1 > prj2 )
{
- if( ( dm - prj1 ) > b->m_h ) return( false );
+ if( ( dm - prj1 ) > b->m_d ) return( false );
}
else
- if( ( dm - prj2 ) > b->m_h ) return( false );
+ if( ( dm - prj2 ) > b->m_d ) return( false );
return( true );
@@ -237,8 +249,8 @@ bool CBoundingBox::_intersects( CBoundingObject* obj, const CVector2D& delta )
bool CBoundingBox::_contains( const CVector2D& point, const CVector2D& delta )
{
- float deltah = fabs( delta.dot( m_u ) );
- if( deltah > m_h ) return( false );
+ float deltad = fabs( delta.dot( m_u ) );
+ if( deltad > m_d ) return( false );
float deltaw = fabs( delta.dot( m_v ) );
if( deltaw > m_w ) return( false );
return( true );
@@ -250,16 +262,16 @@ void CBoundingBox::render( float height )
CVector2D p;
- p = m_pos + m_u * m_h + m_v * m_w;
+ p = m_pos + m_u * m_d + m_v * m_w;
glVertex3f( p.x, height, p.y );
- p = m_pos + m_u * m_h - m_v * m_w;
+ p = m_pos + m_u * m_d - m_v * m_w;
glVertex3f( p.x, height, p.y );
- p = m_pos - m_u * m_h - m_v * m_w;
+ p = m_pos - m_u * m_d - m_v * m_w;
glVertex3f( p.x, height, p.y );
- p = m_pos - m_u * m_h + m_v * m_w;
+ p = m_pos - m_u * m_d + m_v * m_w;
glVertex3f( p.x, height, p.y );
glEnd();
diff --git a/source/simulation/BoundingObjects.h b/source/simulation/BoundingObjects.h
index 09114bbdba..d1018f2428 100755
--- a/source/simulation/BoundingObjects.h
+++ b/source/simulation/BoundingObjects.h
@@ -5,7 +5,6 @@
// Bounding circle and object-aligned bounding box. 2D, for simulation code.
//
// Note: object-aligned bounding boxes are often referred to as oriented bounding boxes (OBBs)
-// At present, this stuff compiles; no guarantee is made of it actually working yet, however.
#ifndef BOUNDING_OBJECTS_INCLUDED
#define BOUNDING_OBJECTS_INCLUDED
@@ -28,7 +27,10 @@ public:
EBoundingType m_type;
CVector2D m_pos;
float m_radius;
+ float m_height;
+
void setPosition( float x, float y );
+ void setHeight( float height );
bool intersects( CBoundingObject* obj );
bool contains( const CVector2D& point );
virtual bool _intersects( CBoundingObject* obj, const CVector2D& delta ) = 0;
@@ -40,7 +42,7 @@ class CBoundingCircle : public CBoundingObject
{
public:
CBoundingCircle() { m_type = BOUND_OABB; }
- CBoundingCircle( float x, float y, float radius );
+ CBoundingCircle( float x, float y, float radius, float height );
CBoundingCircle( float x, float y, CBoundingCircle* copy );
void setRadius( float radius );
bool _intersects( CBoundingObject* obj, const CVector2D& delta );
@@ -52,19 +54,19 @@ class CBoundingBox : public CBoundingObject
{
public:
CBoundingBox() { m_type = BOUND_OABB; }
- CVector2D m_u; // Unit vector along the direction of this box's height.
+ CVector2D m_u; // Unit vector along the direction of this box's depth.
CVector2D m_v; // Unit vector along the direction of this box's width.
- float m_h; // Half this box's height.
+ float m_d; // Half this box's depth.
float m_w; // Half this box's width.
- CBoundingBox( float x, float y, float orientation, float width, float height );
- CBoundingBox( float x, float y, const CVector2D& orientation, float width, float height );
+ CBoundingBox( float x, float y, float orientation, float width, float depth, float height );
+ CBoundingBox( float x, float y, const CVector2D& orientation, float width, float depth, float height );
CBoundingBox( float x, float y, float orientation, CBoundingBox* copy );
CBoundingBox( float x, float y, const CVector2D& orientation, CBoundingBox* copy );
- void setDimensions( float width, float height );
+ void setDimensions( float width, float depth );
void setOrientation( float orientation );
void setOrientation( const CVector2D& orientation );
float getWidth() const { return( 2.0f * m_w ); };
- float getHeight() const { return( 2.0f * m_h ); };
+ float getDepth() const { return( 2.0f * m_d ); };
bool _intersects( CBoundingObject* obj, const CVector2D& delta );
bool _contains( const CVector2D& point, const CVector2D& delta );
void render( float height ); // Temporary
diff --git a/source/simulation/Collision.cpp b/source/simulation/Collision.cpp
index 5d7aba43a4..bcbe0ec7b1 100755
--- a/source/simulation/Collision.cpp
+++ b/source/simulation/Collision.cpp
@@ -23,6 +23,27 @@ CBoundingObject* getContainingObject( const CVector2D& point )
return( NULL );
}
+CEntity* GetCollisionObject( float x, float y )
+{
+ CVector2D point( x, y );
+ std::vector* entities = g_EntityManager.getExtant();
+ std::vector::iterator it;
+
+ for( it = entities->begin(); it != entities->end(); it++ )
+ {
+ if( !(*it)->m_bounds ) continue;
+ if( (*it)->m_bounds->contains( point ) )
+ {
+ CEntity* e = (*it);
+ delete( entities );
+ return( e );
+ }
+ }
+
+ delete( entities );
+ return( NULL );
+}
+
CBoundingObject* getCollisionObject( CBoundingObject* bounds )
{
std::vector* entities = g_EntityManager.getExtant();
@@ -83,8 +104,10 @@ HEntity getCollisionObject( CEntity* entity, float x, float y )
bool getRayIntersection( const CVector2D& source, const CVector2D& forward, const CVector2D& right, float length, float maxDistance, CBoundingObject* destinationCollisionObject, rayIntersectionResults* results )
{
- std::vector* entities = g_EntityManager.getExtant();
- std::vector::iterator it;
+ std::vector entities;
+ g_EntityManager.GetExtant( entities );
+
+ std::vector::iterator it;
float closestApproach, dist;
@@ -93,7 +116,7 @@ bool getRayIntersection( const CVector2D& source, const CVector2D& forward, cons
results->distance = length + maxDistance;
results->boundingObject = NULL;
- for( it = entities->begin(); it != entities->end(); it++ )
+ for( it = entities.begin(); it != entities.end(); it++ )
{
if( !(*it)->m_bounds ) continue;
if( (*it)->m_bounds == destinationCollisionObject ) continue;
@@ -114,12 +137,122 @@ bool getRayIntersection( const CVector2D& source, const CVector2D& forward, cons
results->boundingObject = obj;
results->closestApproach = closestApproach;
results->distance = dist;
- results->hEntity = (*it);
+ results->Entity = (*it);
results->position = obj->m_pos;
}
}
}
- delete( entities );
if( results->boundingObject ) return( true );
return( false );
}
+
+void GetProjectileIntersection( const CVector2D& position, const CVector2D& axis, float length, RayIntersects& results )
+{
+ results.clear();
+
+ std::vector entities;
+ g_EntityManager.GetExtant( entities );
+
+ float dist, closestApproach, l;
+ CVector2D delta;
+
+ std::vector::iterator it;
+ for( it = entities.begin(); it != entities.end(); it++ )
+ {
+ CBoundingObject* obj = (*it)->m_bounds;
+ delta = obj->m_pos - position;
+ closestApproach = delta.betadot( axis );
+ if( fabs( closestApproach ) > obj->m_radius )
+ continue; // Safe, doesn't get close enough.
+ dist = delta.dot( axis );
+ // I just want to see if this will work before I simplify the maths
+ l = sqrt( obj->m_radius * obj->m_radius - closestApproach * closestApproach );
+ if( dist > 0 )
+ {
+ // Forward...
+ if( ( dist - length ) > l )
+ continue; // OK, won't reach it.
+ }
+ else
+ {
+ // Backward...
+ if( -dist > l )
+ continue; // OK, started far enough away
+ }
+
+ if( obj->m_type == CBoundingObject::BOUND_OABB )
+ {
+ // Run a more accurate test against the box
+ CBoundingBox* box = (CBoundingBox*)obj;
+ const float EPSILON = 0.0001f;
+ float first = FLT_MAX, last = -FLT_MAX;
+ CVector2D delta2;
+
+ // Test against those sides of the box parallel with it's u vector.
+ float t = box->m_u.y * axis.x - axis.y * box->m_u.x;
+ float abs_t = fabs( t );
+ if( abs_t >= EPSILON )
+ {
+ // If not parallel,
+ delta2 = delta - box->m_v * box->m_w;
+ if( fabs( axis.y * delta2.x - axis.x * delta2.y ) < box->m_d * abs_t )
+ {
+ // Possible intersection with one side
+ float pos = ( box->m_u.y * delta2.x - box->m_u.x * delta2.y ) / t;
+ if( pos < first ) first = pos;
+ if( pos > last ) last = pos;
+ }
+
+ delta2 = delta + box->m_v * box->m_w;
+ if( fabs( axis.y * delta2.x - axis.x * delta2.y ) < box->m_d * abs_t )
+ {
+ // Possible intersection with one side
+ float pos = ( box->m_u.y * delta2.x - box->m_u.x * delta2.y ) / t;
+ if( pos < first ) first = pos;
+ if( pos > last ) last = pos;
+ }
+ }
+
+ // Next test against those sides of the box parallel with it's v vector.
+ t = box->m_v.y * axis.x - axis.y * box->m_v.x;
+ abs_t = fabs( t );
+ if( abs_t >= EPSILON )
+ {
+ // If not parallel,
+ delta2 = delta - box->m_u * box->m_d;
+ if( fabs( axis.y * delta2.x - axis.x * delta2.y ) < box->m_w * abs_t )
+ {
+ // Possible intersection with one side
+ float pos = ( box->m_v.y * delta2.x - box->m_v.x * delta2.y ) / t;
+ if( pos < first ) first = pos;
+ if( pos > last ) last = pos;
+ }
+
+ delta2 = delta + box->m_u * box->m_d;
+ if( fabs( axis.y * delta2.x - axis.x * delta2.y ) < box->m_w * abs_t )
+ {
+ // Possible intersection with one side
+ float pos = ( box->m_v.y * delta2.x - box->m_v.x * delta2.y ) / t;
+ if( pos < first ) first = pos;
+ if( pos > last ) last = pos;
+ }
+ }
+
+ // Then work out if we actually hit it within the given range.
+ if( last < 0.0f )
+ continue; // No, we started far enough 'after' there.
+ if( first > length )
+ continue; // No, we haven't yet moved far enough to hit it.
+ }
+
+ results.push_back( *it );
+ }
+}
+
+static RayIntersects SharedResults;
+
+RayIntersects& GetProjectileIntersection( const CVector2D& position, const CVector2D& axis, float length )
+{
+ GetProjectileIntersection( position, axis, length, SharedResults );
+ return( SharedResults );
+}
diff --git a/source/simulation/Collision.h b/source/simulation/Collision.h
index f37c4819aa..6675928dc0 100755
--- a/source/simulation/Collision.h
+++ b/source/simulation/Collision.h
@@ -20,17 +20,24 @@
struct rayIntersectionResults
{
- HEntity hEntity;
+ CEntity* Entity;
CBoundingObject* boundingObject;
CVector2D position;
float closestApproach;
float distance;
};
+typedef std::vector RayIntersects;
+
HEntity getCollisionObject( CEntity* entity );
HEntity getCollisionObject( CEntity* entity, float x, float y );
CBoundingObject* getCollisionObject( CBoundingObject* bounds );
CBoundingObject* getContainingObject( const CVector2D& point );
+CEntity* GetCollisionObject( float x, float y ); // Point collision
bool getRayIntersection( const CVector2D& source, const CVector2D& forward, const CVector2D& right, float length, float maxDistance, CBoundingObject* destinationCollisionObject, rayIntersectionResults* results );
+// Assumes zero width, also includes moving units (other one doesn't).
+void GetProjectileIntersection( const CVector2D& position, const CVector2D& axis, float length, RayIntersects& results );
+// Stores results in shared area
+RayIntersects& GetProjectileIntersection( const CVector2D& position, const CVector2D& axis, float length );
#endif
diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp
index ffdd8629f4..8e9788efec 100755
--- a/source/simulation/Entity.cpp
+++ b/source/simulation/Entity.cpp
@@ -574,7 +574,7 @@ void CEntity::renderSelectionOutline( float alpha )
CVector2D p, q;
CVector2D u, v;
q.x = pos.X; q.y = pos.Z;
- float h = ((CBoundingBox*)m_bounds)->m_h;
+ float d = ((CBoundingBox*)m_bounds)->m_d;
float w = ((CBoundingBox*)m_bounds)->m_w;
u.x = sin( m_graphics_orientation );
@@ -585,25 +585,25 @@ void CEntity::renderSelectionOutline( float alpha )
#ifdef SELECTION_TERRAIN_CONFORMANCE
for( int i = SELECTION_BOX_POINTS; i > -SELECTION_BOX_POINTS; i-- )
{
- p = q + u * h + v * ( w * (float)i / (float)SELECTION_BOX_POINTS );
+ p = q + u * d + v * ( w * (float)i / (float)SELECTION_BOX_POINTS );
glVertex3f( p.x, pTerrain->getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
}
for( int i = SELECTION_BOX_POINTS; i > -SELECTION_BOX_POINTS; i-- )
{
- p = q + u * ( h * (float)i / (float)SELECTION_BOX_POINTS ) - v * w;
+ p = q + u * ( d * (float)i / (float)SELECTION_BOX_POINTS ) - v * w;
glVertex3f( p.x, pTerrain->getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
}
for( int i = -SELECTION_BOX_POINTS; i < SELECTION_BOX_POINTS; i++ )
{
- p = q - u * h + v * ( w * (float)i / (float)SELECTION_BOX_POINTS );
+ p = q - u * d + v * ( w * (float)i / (float)SELECTION_BOX_POINTS );
glVertex3f( p.x, pTerrain->getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
}
for( int i = -SELECTION_BOX_POINTS; i < SELECTION_BOX_POINTS; i++ )
{
- p = q + u * ( h * (float)i / (float)SELECTION_BOX_POINTS ) + v * w;
+ p = q + u * ( d * (float)i / (float)SELECTION_BOX_POINTS ) + v * w;
glVertex3f( p.x, pTerrain->getExactGroundLevel( p.x, p.y ) + 0.25f, p.y );
}
#else
@@ -887,7 +887,7 @@ jsval CEntity::GetSpawnPoint( JSContext* cx, uintN argc, jsval* argv )
assert( 0 && "No arguments to Entity::GetSpawnPoint()" );
// TODO: Make netsafe.
- CBoundingCircle spawn( 0.0f, 0.0f, spawn_clearance );
+ CBoundingCircle spawn( 0.0f, 0.0f, spawn_clearance, 0.0f );
if( m_bounds->m_type == CBoundingObject::BOUND_OABB )
{
@@ -898,22 +898,22 @@ jsval CEntity::GetSpawnPoint( JSContext* cx, uintN argc, jsval* argv )
int edge = rand() & 3; int point;
double max_w = oabb->m_w + spawn_clearance + 1.0;
- double max_h = oabb->m_h + spawn_clearance + 1.0;
+ double max_d = oabb->m_d + spawn_clearance + 1.0;
int w_count = (int)( max_w * 2 );
- int h_count = (int)( max_h * 2 );
+ int d_count = (int)( max_d * 2 );
CVector2D w_step = oabb->m_v * (float)( max_w / w_count );
- CVector2D h_step = oabb->m_u * (float)( max_h / h_count );
+ CVector2D d_step = oabb->m_u * (float)( max_d / d_count );
CVector2D pos( m_position );
if( edge & 1 )
{
- point = rand() % ( 2 * h_count ) - h_count;
- pos += ( oabb->m_v * (float)max_w + h_step * (float)point ) * ( ( edge & 2 ) ? -1.0f : 1.0f );
+ point = rand() % ( 2 * d_count ) - d_count;
+ pos += ( oabb->m_v * (float)max_w + d_step * (float)point ) * ( ( edge & 2 ) ? -1.0f : 1.0f );
}
else
{
point = rand() % ( 2 * w_count ) - w_count;
- pos += ( oabb->m_u * (float)max_h + w_step * (float)point ) * ( ( edge & 2 ) ? -1.0f : 1.0f );
+ pos += ( oabb->m_u * (float)max_d + w_step * (float)point ) * ( ( edge & 2 ) ? -1.0f : 1.0f );
}
int start_edge = edge; int start_point = point;
@@ -931,12 +931,12 @@ jsval CEntity::GetSpawnPoint( JSContext* cx, uintN argc, jsval* argv )
if( point >= w_count )
{
edge = 1;
- point = -h_count;
+ point = -d_count;
}
break;
case 1:
- point++; pos -= h_step;
- if( point >= h_count )
+ point++; pos -= d_step;
+ if( point >= d_count )
{
edge = 2;
point = w_count;
@@ -947,12 +947,12 @@ jsval CEntity::GetSpawnPoint( JSContext* cx, uintN argc, jsval* argv )
if( point <= -w_count )
{
edge = 3;
- point = h_count;
+ point = d_count;
}
break;
case 3:
- point--; pos += h_step;
- if( point <= -h_count )
+ point--; pos += d_step;
+ if( point <= -d_count )
{
edge = 0;
point = -w_count;
diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h
index 111a430dfa..c6d76fa188 100755
--- a/source/simulation/Entity.h
+++ b/source/simulation/Entity.h
@@ -7,8 +7,7 @@
// Usage: Do not attempt to instantiate this class directly. (See EntityManager.h)
// Most of the members are trivially obvious; some highlights are:
//
-// HEntity me: is a reference to this entity. Use instead of the address-of operator for
-// non-temporary references. See EntityHandles.h
+// HEntity me: is a reference to this entity. See EntityHandles.h
//
// Destroying entities: An entity is destroyed when all references to it expire.
// It is somewhat unfunny if this happens while a method from this
@@ -18,15 +17,11 @@
// prior to its next update cycle.
//
// CUnit* m_actor: is the visible representation of this entity.
-// std::hash_map m_properties: isn't yet used, is capable of storing properties defined by script.
//
// snapToGround(): Called every frame, this will ensure the entity never takes flight.
// updateActorTransforms(): Must be called every time the position of this entity changes.
// Also remember to update the collision object if you alter the position directly.
//
-// Some notes: update() and dispatch() /can/ be called directly without ill effects,
-// but it's preferable to go through the Entity manager and the Scheduler, respectively.
-//
#ifndef ENTITY_INCLUDED
#define ENTITY_INCLUDED
diff --git a/source/simulation/EntityManager.cpp b/source/simulation/EntityManager.cpp
index ff0274be83..118ab0414c 100755
--- a/source/simulation/EntityManager.cpp
+++ b/source/simulation/EntityManager.cpp
@@ -1,3 +1,4 @@
+
#include "precompiled.h"
#include "EntityManager.h"
@@ -97,6 +98,14 @@ std::vector* CEntityManager::getExtant()
return( activelist );
}
+void CEntityManager::GetExtant( std::vector& results )
+{
+ results.clear();
+ for( int i = 0; i < MAX_HANDLES; i++ )
+ if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed )
+ results.push_back( m_entities[i].m_entity );
+}
+
/*
void CEntityManager::dispatchAll( CMessage* msg )
{
diff --git a/source/simulation/EntityManager.h b/source/simulation/EntityManager.h
index 5ed5e5f715..8fa79ddab5 100755
--- a/source/simulation/EntityManager.h
+++ b/source/simulation/EntityManager.h
@@ -68,6 +68,7 @@ public:
std::vector* matches( EntityPredicate predicate, void* userdata = NULL );
std::vector* getExtant();
+ void GetExtant( std::vector& results ); // TODO: Switch most/all uses of getExtant() to this.
static inline bool extant() // True if the singleton is actively maintaining handles. When false, system is shutting down, handles are quietly dumped.
{
return( m_extant );
diff --git a/source/simulation/EntityStateProcessing.cpp b/source/simulation/EntityStateProcessing.cpp
index c810cf8ea1..bff0a8220b 100755
--- a/source/simulation/EntityStateProcessing.cpp
+++ b/source/simulation/EntityStateProcessing.cpp
@@ -3,6 +3,7 @@
#include "precompiled.h"
#include "Entity.h"
+#include "BaseEntity.h"
#include "Model.h"
#include "ObjectEntry.h"
#include "Unit.h"
@@ -146,7 +147,7 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis,
if( ( m_orderQueue.size() == 1 ) && ( len <= 10.0f ) )
{
- CBoundingCircle destinationObs( current->m_data[0].location.x, current->m_data[0].location.y, m_bounds->m_radius );
+ CBoundingCircle destinationObs( current->m_data[0].location.x, current->m_data[0].location.y, m_bounds->m_radius, 0.0f );
if( getCollisionObject( &destinationObs ) )
{
// Yes. (Chances are a bunch of units were tasked to the same destination)
@@ -206,7 +207,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli
{
// Here's a wierd idea: (I hope it works)
// Spiral round the destination until a free point is found.
- CBoundingCircle destinationObs( current->m_data[0].location.x, current->m_data[0].location.y, m_bounds->m_radius );
+ CBoundingCircle destinationObs( current->m_data[0].location.x, current->m_data[0].location.y, m_bounds->m_radius, 0.0f );
float interval = destinationObs.m_radius;
float r = interval, theta = 0.0f, delta;
@@ -312,9 +313,16 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
m_actor->GetModel()->SetAnimation( m_fsm_animation, true );
}
-
+ if( ( m_fsm_cyclepos <= m_fsm_anipos2 ) &&
+ ( nextpos > m_fsm_anipos2 ) )
+ {
+ // Load the ammunition.
+ m_actor->ShowAmmunition();
+ }
if( ( m_fsm_cyclepos <= action->m_Speed ) && ( nextpos > action->m_Speed ) )
{
+ // Fire!
+ m_actor->HideAmmunition();
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.
@@ -448,6 +456,22 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
m_actor->GetModel()->SetAnimation( m_fsm_animation, true );
m_actor->GetModel()->Update( m_fsm_animation->m_ActionPos / 1000.0f - action->m_Speed / 2000.0f );
}
+ else
+ {
+ // If we've just transitioned, play idle. Otherwise, let the previous animation complete, if it
+ // hasn't already.
+ if( m_transition )
+ m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_IdleAnim );
+ }
+
+ // Load time needs to be animation->m_ActionPos2 ms after the start of the animation.
+
+ m_fsm_anipos2 = m_fsm_anipos + ( m_fsm_animation->m_ActionPos2 * 2 );
+ if( action->m_Speed < ( ( m_fsm_animation->m_ActionPos + m_fsm_animation->m_ActionPos2 ) * 2 ) )
+ {
+ // Load now.
+ m_actor->ShowAmmunition();
+ }
m_fsm_cyclepos = 0;
@@ -461,7 +485,15 @@ bool CEntity::processAttackMelee( CEntityOrder* current, size_t timestep_millis
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_melee ) );
+ if( !m_actor ) return( false );
+ CSkeletonAnim* animation = m_actor->GetObject()->m_MeleeAnim;
+ if( !animation ) animation = m_actor->GetObject()->m_IdleAnim;
+ if( !animation ) return( false ); // Should probably tell people why this is failing
+ // (didn't specify an actor or animation) but that
+ // would probably involve including CLogger.h, which
+ // conflicts.
+
+ return( processContactActionNoPathing( current, timestep_milli, animation, &evt, &m_melee ) );
}
bool CEntity::processGather( CEntityOrder* current, size_t timestep_millis )
{
@@ -471,7 +503,14 @@ bool CEntity::processGather( CEntityOrder* current, size_t timestep_millis )
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, &m_gather ) );
+ if( !m_actor ) return( false );
+ CSkeletonAnim* animation = m_actor->GetObject()->m_GatherAnim;
+ if( !animation ) animation = m_actor->GetObject()->m_IdleAnim;
+ if( !animation ) return( false ); // Should probably tell people why this is failing
+ // (didn't specify an actor or animation) but that
+ // would probably involve including CLogger.h, which
+ // conflicts.
+ return( processContactActionNoPathing( current, timestep_millis, animation, &evt, &m_gather ) );
}
bool CEntity::processGoto( CEntityOrder* current, size_t timestep_millis )
diff --git a/source/simulation/Projectile.cpp b/source/simulation/Projectile.cpp
new file mode 100644
index 0000000000..7de93b1baa
--- /dev/null
+++ b/source/simulation/Projectile.cpp
@@ -0,0 +1,266 @@
+#include "precompiled.h"
+
+#include "Projectile.h"
+#include "Entity.h"
+#include "Model.h"
+#include "Unit.h"
+#include "Matrix3D.h"
+#include "ScriptObject.h"
+#include "Game.h"
+#include "Collision.h"
+#include "ObjectManager.h"
+#include "CLogger.h"
+
+const double GRAVITY = 0.0000002;
+const double GRAVITY_2 = GRAVITY * 0.5;
+
+CProjectile::CProjectile( const CModel* Actor, const CVector3D& Position, const CVector3D& Target, float Speed, CEntity* Originator, const CScriptObject& ImpactScript, const CScriptObject& MissScript )
+{
+ m_Actor = Actor->Clone();
+ m_Position = m_Position_Previous = m_Position_Graphics = Position;
+ m_Speed_H = Speed;
+ m_Originator = Originator;
+ m_ImpactEventHandler = ImpactScript;
+ m_MissEventHandler = MissScript;
+
+ AddHandler( EVENT_IMPACT, &m_ImpactEventHandler );
+ AddHandler( EVENT_MISS, &m_MissEventHandler );
+
+ // That was the easy stuff.
+ // We want horizontal distance only:
+
+ m_Axis = Target - Position;
+ double s = m_Axis.length();
+ m_Axis /= (float)s;
+
+ // Now vertical distance:
+ double d_h = Target.Y - Position.Y;
+ // Time of impact:
+ double t = s / m_Speed_H;
+ // Required vertical velocity at launch:
+ m_Speed_V = (float)( d_h / t + GRAVITY_2 * t );
+}
+
+CProjectile::~CProjectile()
+{
+ std::vector::iterator it;
+ for( it = g_ProjectileManager.m_Projectiles.begin(); it != g_ProjectileManager.m_Projectiles.end(); ++it )
+ if( *it == this )
+ {
+ g_ProjectileManager.m_Projectiles.erase( it );
+ break;
+ }
+ delete( m_Actor );
+}
+
+bool CProjectile::Update( size_t timestep_millis )
+{
+ m_Position_Previous = m_Position;
+ m_Position.X += timestep_millis * m_Axis.x * m_Speed_H;
+ m_Position.Z += timestep_millis * m_Axis.y * m_Speed_H;
+
+ m_Position.Y += (float)( timestep_millis * ( m_Speed_V - timestep_millis * GRAVITY_2 ) );
+ m_Speed_V -= (float)( timestep_millis * GRAVITY );
+
+ float height = m_Position.Y - g_Game->GetWorld()->GetTerrain()->getExactGroundLevel( m_Position.X, m_Position.Z );
+
+ if( height < 0.0f )
+ {
+ // We appear to have missed.
+ CEventProjectileMiss evt( m_Originator, m_Position );
+ DispatchEvent( &evt );
+ // Not going to let this be cancelled.
+ return( false );
+ }
+ RayIntersects& r = GetProjectileIntersection( m_Position_Previous, m_Axis, timestep_millis * m_Speed_H );
+ RayIntersects::iterator it;
+ for( it = r.begin(); it != r.end(); it++ )
+ {
+ // Hit something?
+ if( *it != m_Originator ) /* That wouldn't be fair at all... */
+ {
+ // Low enough to hit it?
+ if( height < (*it)->m_bounds->m_height )
+ {
+ CEventProjectileImpact evt( m_Originator, *it, m_Position );
+ if( DispatchEvent( &evt ) )
+ return( false );
+ }
+ }
+ }
+ return( true );
+}
+
+void CProjectile::Interpolate( size_t timestep_millis )
+{
+ m_Position_Graphics.X = m_Position_Previous.X + timestep_millis * m_Speed_H * m_Axis.x;
+ m_Position_Graphics.Z = m_Position_Previous.Z + timestep_millis * m_Speed_H * m_Axis.y;
+ m_Position_Graphics.Y = (float)( m_Position_Previous.Y + timestep_millis * ( m_Speed_V - timestep_millis * GRAVITY_2 ) );
+ float dh_dt = (float)( m_Speed_V - timestep_millis * GRAVITY );
+ float scale = 1 / sqrt( m_Speed_H * m_Speed_H + dh_dt * dh_dt );
+ float scale2 = m_Speed_H * scale;
+
+ float y = dh_dt * scale;
+ CMatrix3D rotateInc;
+ rotateInc.SetIdentity();
+ rotateInc._22 = rotateInc._33 = y;
+ rotateInc._23 = -( rotateInc._32 = scale2 );
+
+ CMatrix3D rotateDir;
+ rotateDir.SetIdentity();
+ rotateDir._11 = rotateDir._33 = m_Axis.y;
+ rotateDir._31 = -( rotateDir._13 = m_Axis.x );
+ rotateInc.Concatenate( rotateDir );
+
+ rotateInc._14 = m_Position_Graphics.X;
+ rotateInc._24 = m_Position_Graphics.Y;
+ rotateInc._34 = m_Position_Graphics.Z;
+
+ m_Actor->SetTransform( rotateInc );
+}
+
+void CProjectile::ScriptingInit()
+{
+ CJSObject::ScriptingInit( "Projectile", Construct, 4 );
+}
+
+JSBool CProjectile::Construct( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval )
+{
+ assert( argc >= 4 );
+ CStr ModelString;
+ CVector3D Here, There;
+ float Speed;
+ CEntity* Temp, *Originator = NULL;
+ CObjectEntry* oe = NULL;
+ CModel* Model = NULL;
+ CScriptObject Impact, Miss;
+ const char* err = NULL;
+
+
+ if( Temp = ToNative( argv[0] ) )
+ {
+ Model = Temp->m_actor->GetObject()->m_ProjectileModel;
+ if( !Model )
+ {
+ err = "No projectile model is defined for that entity's actor.";
+ goto fail;
+ }
+ }
+ else if( !ToPrimitive( cx, argv[0], ModelString ) || !( oe = g_ObjMan.FindObject( ModelString ) ) || !( Model = oe->m_Model ) )
+ {
+ err = "Invalid actor";
+ goto fail;
+ }
+ if( Temp = ToNative( argv[1] ) )
+ {
+ // Use the position vector of this entity, add a bit (so the arrow doesn't appear out of the ground)
+ // In future, find the appropriate position from the entity (location of a specific prop point?)
+ Here = Temp->m_position;
+ Here.Y = g_Game->GetWorld()->GetTerrain()->getExactGroundLevel( Here.X, Here.Z ) + 2.5f;
+ }
+ else if( !( ToPrimitive( cx, argv[1], Here ) ) )
+ {
+ err = "Invalid vector";
+ goto fail;
+ }
+ if( Temp = ToNative( argv[2] ) )
+ {
+ // Use the position vector of this entity.
+ // TODO: Maybe: Correct for the movement of this entity.
+ // Then again, that doesn't belong here.
+ There = Temp->m_position;
+ There.Y = g_Game->GetWorld()->GetTerrain()->getExactGroundLevel( There.X, There.Z ) + 2.5f;
+ }
+ else if( !( ToPrimitive( cx, argv[2], There ) ) )
+ {
+ err = "Invalid vector";
+ goto fail;
+ }
+ if( ( Speed = ToPrimitive( cx, argv[3] ) ) == 0.0f )
+ {
+ // Either wasn't specified, or was zero. In either case,
+ // can't allow it: div/0 errors in the physics
+ err = "Invalid speed";
+ goto fail;
+ }
+ // Ignore errors in these last few and use the defaults if there's a problem.
+
+ if( argc >= 5 )
+ Originator = ToNative( argv[4] );
+ if( argc >= 6 )
+ Impact = argv[5]; // Script to run on impact with an entity.
+ if( argc >= 7 )
+ Miss = argv[6]; // Script to run on impact with the floor.
+
+ CProjectile* p = g_ProjectileManager.AddProjectile( Model, Here, There, Speed / 1000.0f, Originator, Impact, Miss );
+
+ *rval = ToJSVal( *p );
+ return( JS_TRUE );
+
+fail:
+ *rval = JSVAL_NULL;
+ JS_ReportError( cx, err );
+ return( JS_TRUE );
+}
+
+CEventProjectileImpact::CEventProjectileImpact( CEntity* Originator, CEntity* Impact, const CVector3D& Position ) : CScriptEvent( L"ProjectileImpact", EVENT_IMPACT )
+{
+ m_Originator = Originator;
+ m_Impact = Impact;
+ m_Position = Position;
+ AddLocalProperty( L"originator", &m_Originator, true );
+ AddLocalProperty( L"impacted", &m_Impact );
+ AddLocalProperty( L"position", &m_Position );
+}
+
+CEventProjectileMiss::CEventProjectileMiss( CEntity* Originator, const CVector3D& Position ) : CScriptEvent( L"ProjectileMiss", EVENT_MISS )
+{
+ m_Originator = Originator;
+ m_Position = Position;
+ AddLocalProperty( L"originator", &m_Originator, true );
+ AddLocalProperty( L"position", &m_Position );
+}
+
+CProjectileManager::CProjectileManager()
+{
+ m_LastTurnLength = 0;
+ debug_out( "CProjectileManager CREATED\n" );
+}
+
+CProjectileManager::~CProjectileManager()
+{
+ while( m_Projectiles.size() )
+ delete( m_Projectiles[0] );
+ debug_out( "CProjectileManager DESTROYED\n" );
+}
+
+CProjectile* CProjectileManager::AddProjectile( const CModel* Actor, const CVector3D& Position, const CVector3D& Target, float Speed, CEntity* Originator, const CScriptObject& ImpactScript, const CScriptObject& MissScript )
+{
+ CProjectile* p = new CProjectile( Actor, Position, Target, Speed, Originator, ImpactScript, MissScript );
+ m_Projectiles.push_back( p );
+ return( p );
+}
+
+void CProjectileManager::DeleteProjectile( CProjectile* p )
+{
+ delete( p );
+}
+
+void CProjectileManager::UpdateAll( size_t timestep )
+{
+ uint i;
+ for( i = 0; i < m_Projectiles.size(); i++ )
+ if( !( m_Projectiles[i]->Update( timestep ) ) )
+ {
+ delete( m_Projectiles[i] );
+ i--;
+ }
+}
+
+void CProjectileManager::InterpolateAll( double relativeOffset )
+{
+ size_t absoluteOffset = (size_t)( (double)m_LastTurnLength * relativeOffset );
+ std::vector::iterator it;
+ for( it = m_Projectiles.begin(); it != m_Projectiles.end(); ++it )
+ (*it)->Interpolate( absoluteOffset );
+}
\ No newline at end of file
diff --git a/source/simulation/Projectile.h b/source/simulation/Projectile.h
new file mode 100644
index 0000000000..c5b54a883e
--- /dev/null
+++ b/source/simulation/Projectile.h
@@ -0,0 +1,107 @@
+// Projectile.h
+//
+// Simple class that represents a single projectile in the simulation.
+//
+// Mark Thompson (mot20@cam.ac.uk / mark@wildfiregames.com)
+
+#ifndef PROJECTILE_INCLUDED
+#define PROJECTILE_INCLUDED
+
+#include "Vector3D.h"
+#include "Vector2D.h"
+#include "Singleton.h"
+#include "Scripting/ScriptableObject.h"
+#include "scripting/DOMEvent.h"
+#include "ScriptObject.h"
+
+class CProjectileManager;
+class CModel;
+class CEntity;
+
+class CProjectile : public CJSObject, public IEventTarget
+{
+ friend class CProjectileManager;
+ friend class CJSObject;
+
+ CModel* m_Actor;
+
+ CVector3D m_Position;
+ CVector2D m_Axis;
+
+ CVector3D m_Position_Previous;
+ CVector3D m_Position_Graphics;
+
+ // Horizontal and vertical velocities
+ float m_Speed_H;
+ float m_Speed_V;
+
+ CEntity* m_Originator;
+
+ CScriptObject m_ImpactEventHandler;
+ CScriptObject m_MissEventHandler;
+
+ CProjectile( const CModel* Actor, const CVector3D& Position, const CVector3D& Target, float Speed, CEntity* Originator, const CScriptObject& ImpactScript, const CScriptObject& MissScript );
+ ~CProjectile();
+
+ // Updates gameplay information for the specified timestep. Returns 'false' if the projectile should be removed from the world.
+ bool Update( size_t timestep_millis );
+ // Updates graphical information for a point timestep_millis after the previous simulation frame (and before the current one)
+ void Interpolate( size_t timestep_millis );
+
+ // Scripty things.
+public:
+ static void ScriptingInit();
+ static JSBool Construct( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval );
+ JSObject* GetScriptExecContext( IEventTarget* target ) { return( GetScript() ); }
+
+ inline CModel* GetModel() const { return( m_Actor ); }
+};
+
+class CEventProjectileImpact : public CScriptEvent
+{
+ CEntity* m_Originator;
+ CEntity* m_Impact;
+ CVector3D m_Position;
+public:
+ CEventProjectileImpact( CEntity* Originator, CEntity* Impact, const CVector3D& Position );
+};
+
+class CEventProjectileMiss : public CScriptEvent
+{
+ CEntity* m_Originator;
+ CVector3D m_Position;
+public:
+ CEventProjectileMiss( CEntity* Originator, const CVector3D& Position );
+};
+
+// TODO: Maybe roll this (or at least the graphics bit) into the particle system?
+// Initially this had g_UnitMan managing the models and g_EntityManager holding the
+// projectiles themselves. This way may be less confusing - I'm not sure.
+
+class CProjectileManager : public Singleton
+{
+ friend class CProjectile;
+public:
+ CProjectileManager();
+ ~CProjectileManager();
+
+ void UpdateAll( size_t timestep );
+ void InterpolateAll( double frametime );
+
+ inline const std::vector& GetProjectiles() { return m_Projectiles; }
+
+ CProjectile* AddProjectile( const CModel* Actor, const CVector3D& Position, const CVector3D& Target, float Speed, CEntity* Originator, const CScriptObject& ImpactScript, const CScriptObject& MissScript );
+ // Only if you have some reason to prematurely get rid of a projectile.
+ // Under normal circumstances, it will delete itself when it hits something.
+ void DeleteProjectile( CProjectile* p );
+private:
+ // Keep this so we can go from relative->absolute offsets in interpolate.
+ size_t m_LastTurnLength;
+
+ // Maintain a list of the projectiles in the world
+ std::vector m_Projectiles;
+};
+
+#define g_ProjectileManager CProjectileManager::GetSingleton()
+
+#endif
diff --git a/source/simulation/ScriptObject.cpp b/source/simulation/ScriptObject.cpp
index 5be0b42470..66afb454f7 100755
--- a/source/simulation/ScriptObject.cpp
+++ b/source/simulation/ScriptObject.cpp
@@ -31,6 +31,7 @@ void CScriptObject::Uproot()
CScriptObject::CScriptObject( JSFunction* _Function )
{
+ Function = NULL;
SetFunction( _Function );
}
@@ -40,6 +41,12 @@ CScriptObject::CScriptObject( jsval v )
SetJSVal( v );
}
+CScriptObject::CScriptObject( const CScriptObject& copy )
+{
+ Function = NULL;
+ SetFunction( copy.Function );
+}
+
void CScriptObject::SetFunction( JSFunction* _Function )
{
Uproot();
diff --git a/source/simulation/ScriptObject.h b/source/simulation/ScriptObject.h
index 968be382be..b63bc0402a 100755
--- a/source/simulation/ScriptObject.h
+++ b/source/simulation/ScriptObject.h
@@ -19,11 +19,15 @@ class CScriptObject
void Root();
void Uproot();
+ // TODO: Remove, debugging
+ static int count;
+
public:
CScriptObject();
CScriptObject( JSFunction* _Function );
CScriptObject( jsval v );
+ CScriptObject( const CScriptObject& copy );
~CScriptObject();
diff --git a/source/simulation/Simulation.cpp b/source/simulation/Simulation.cpp
index b67bbeb547..2c7943040f 100755
--- a/source/simulation/Simulation.cpp
+++ b/source/simulation/Simulation.cpp
@@ -7,6 +7,7 @@
#include "TurnManager.h"
#include "Game.h"
#include "EntityManager.h"
+#include "Projectile.h"
#include "Scheduler.h"
#include "Network/NetMessage.h"
#include "CLogger.h"
@@ -66,6 +67,21 @@ void CSimulation::Update(double frameTime)
// frames as fast as possible.
m_DeltaTime = 0.0;
}
+
+ /*
+ // TODO Remove
+ // Fountain of arrows
+ CObjectEntry* arrow = g_ObjMan.FindObject( "props/weapon/weap_arrow_front.xml" );
+ assert( arrow );
+
+ float mapsize = (float)( m_pWorld->GetTerrain()->GetVerticesPerSide() * 4 );
+
+ CVector3D here( mapsize / 2, m_pWorld->GetTerrain()->getExactGroundLevel( mapsize / 2, mapsize / 2 ), mapsize / 2 );
+ float x = ( rand() % 1000 ) * mapsize / 1000.0f;
+ float y = ( rand() % 1000 ) * mapsize / 1000.0f;
+ CVector3D there( x, m_pWorld->GetTerrain()->getExactGroundLevel( x, y ), y );
+ g_ProjectileManager.AddProjectile( arrow->m_Model, here, there, 0.006f, NULL );
+ */
}
PROFILE_START( "simulation interpolation" );
@@ -79,7 +95,8 @@ void CSimulation::Interpolate(double frameTime, double offset)
for (uint i=0;iGetModel()->Update((float)frameTime);
- g_EntityManager.interpolateAll((float)offset);
+ g_EntityManager.interpolateAll( (float)offset );
+ g_ProjectileManager.InterpolateAll( (float)offset );
}
void CSimulation::Simulate()
@@ -92,6 +109,10 @@ void CSimulation::Simulate()
g_EntityManager.updateAll( m_pTurnManager->GetTurnLength() );
PROFILE_END( "entity updates" );
+ PROFILE_START( "projectile updates" );
+ g_ProjectileManager.UpdateAll( m_pTurnManager->GetTurnLength() );
+ PROFILE_END( "projectile updates" );
+
PROFILE_START( "turn manager update" );
m_pTurnManager->NewTurn();
m_pTurnManager->IterateBatch(0, TranslateMessage, this);