1
0
forked from 0ad/0ad

Projectile code and updates to some actors that can use it (celt_ijv and hele_iar)

This was SVN commit r2266.
This commit is contained in:
MarkT 2005-05-10 07:13:25 +00:00
parent 28b92f3c43
commit 86dc351205
71 changed files with 1057 additions and 154 deletions

View File

@ -12,13 +12,14 @@
<animation file="biped/dudechop.psa" name="Chop" speed="150"/>
<animation file="biped/dudedeath_sword.psa" name="Death" speed="200"/>
<animation file="biped/dudedecay_sword.psa" name="Decay" speed="100"/>
<animation file="biped/inf_jav_atk_a.psa" name="Attack" speed="400"/>
<animation file="biped/inf_jav_atk_a.psa" name="Attack" speed="400" event="0.35" load="0"/>
</animations>
<mesh>skeletal/m_pants_b.pmd</mesh>
<props>
<prop actor="props/head/head_lime.xml" attachpoint="head"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="r_hand"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="loaded-r_hand"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="l_hand"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="projectile"/>
</props>
</variant>
</group>

View File

@ -12,13 +12,14 @@
<animation file="biped/dudechop.psa" name="Chop" speed="150"/>
<animation file="biped/dudedeath_sword.psa" name="Death" speed="200"/>
<animation file="biped/dudedecay_sword.psa" name="Decay" speed="100"/>
<animation file="biped/inf_jav_atk_a.psa" name="Attack" speed="400"/>
<animation file="biped/inf_jav_atk_a.psa" name="Attack" speed="400" event="0.35" load="0"/>
</animations>
<mesh>skeletal/m_pants_b.pmd</mesh>
<props>
<prop actor="props/head/head_celt.xml" attachpoint="head"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="r_hand"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="loaded-r_hand"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="l_hand"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="projectile"/>
</props>
<texture>skeletal/celt_ijv_b_01.dds</texture>
</variant>

View File

@ -12,14 +12,15 @@
<animation file="biped/dudechop.psa" name="Chop" speed="150"/>
<animation file="biped/dudedeath_sword.psa" name="Death" speed="200"/>
<animation file="biped/dudedecay_sword.psa" name="Decay" speed="100"/>
<animation file="biped/inf_jav_atk_a.psa" name="Attack" speed="400"/>
<animation file="biped/inf_jav_atk_a.psa" name="Attack" speed="400" event="0.35" load="0"/>
</animations>
<mesh>skeletal/m_tights.pmd</mesh>
<props>
<prop actor="props/head/celt_helmet_b.xml" attachpoint="helmet"/>
<prop actor="props/head/head_celt.xml" attachpoint="head"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="r_hand"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="loaded-r_hand"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="l_hand"/>
<prop actor="props/weapon/weap_jav.xml" attachpoint="projectile"/>
</props>
<texture>skeletal/celt_ijv_e_01.dds</texture>
</variant>

View File

@ -12,7 +12,7 @@
<animation file="biped/dudechop.psa" name="Chop" speed="150"/>
<animation file="biped/dudedeath_sword.psa" name="Death" speed="200"/>
<animation file="biped/dudedecay_sword.psa" name="Decay" speed="100"/>
<animation file="biped/inf_arch_atk_a.psa" name="Attack" speed="400"/>
<animation file="biped/inf_arch_atk_a.psa" name="Attack" speed="400" event="0.75" load="0.4"/>
</animations>
<mesh>skeletal/m_tunic_b.pmd</mesh>
<props>
@ -20,6 +20,8 @@
<prop actor="props/head/head_hele_a.xml" attachpoint="head"/>
<prop actor="props/weapon/weap_bow_short.xml" attachpoint="l_hand"/>
<prop actor="props/temp/quiver.xml" attachpoint="back"/>
<prop actor="props/weapon/weap_arrow_back.xml" attachpoint="loaded-r_hand" />
<prop actor="props/weapon/weap_arrow_front.xml" attachpoint="projectile" />
</props>
<texture>skeletal/hele_isp_b.dds</texture>
</variant>

View File

@ -12,7 +12,7 @@
<animation file="biped/dudechop.psa" name="Chop" speed="150"/>
<animation file="biped/dudedeath_sword.psa" name="Death" speed="200"/>
<animation file="biped/dudedecay_sword.psa" name="Decay" speed="100"/>
<animation file="biped/inf_arch_atk_a.psa" name="Attack" speed="400"/>
<animation file="biped/inf_arch_atk_a.psa" name="Attack" speed="400" event="0.75" load="0.4"/>
</animations>
<mesh>skeletal/m_tunic_b.pmd</mesh>
<props>
@ -20,6 +20,8 @@
<prop actor="props/head/head_hele_b.xml" attachpoint="head"/>
<prop actor="props/weapon/weap_bow_short.xml" attachpoint="l_hand"/>
<prop actor="props/temp/quiver.xml" attachpoint="back"/>
<prop actor="props/weapon/weap_arrow_back.xml" attachpoint="loaded-r_hand" />
<prop actor="props/weapon/weap_arrow_front.xml" attachpoint="projectile" />
</props>
<texture>skeletal/hele_iar_b.dds</texture>
</variant>

View File

@ -12,7 +12,7 @@
<animation file="biped/dudechop.psa" name="Chop" speed="150"/>
<animation file="biped/dudedeath_sword.psa" name="Death" speed="200"/>
<animation file="biped/dudedecay_sword.psa" name="Decay" speed="100"/>
<animation file="biped/inf_arch_atk_a.psa" name="Attack" speed="400"/>
<animation file="biped/inf_arch_atk_a.psa" name="Attack" speed="400" event="0.75" load="0.4"/>
</animations>
<mesh>skeletal/m_tunic_b.pmd</mesh>
<props>
@ -21,6 +21,8 @@
<prop actor="props/head/plac_head_hele.xml" attachpoint="head"/>
<prop actor="props/weapon/weap_bow_short.xml" attachpoint="l_hand"/>
<prop actor="props/temp/quiver.xml" attachpoint="back"/>
<prop actor="props/weapon/weap_arrow_back.xml" attachpoint="loaded-r_hand" />
<prop actor="props/weapon/weap_arrow_front.xml" attachpoint="projectile" />
</props>
<texture>skeletal/plac_hele_e.dds</texture>
</variant>

View File

@ -21,5 +21,5 @@
subtype="meat"
/>
</Traits>
<Footprint Radius="1.0"/>
<Footprint Radius="1.0" Height="2.5"/>
</Entity>

View File

@ -9,5 +9,5 @@
classes="gaia_flora"
/>
</Traits>
<Footprint Radius="1.0"/>
<Footprint Radius="1.0" Height="10.0"/>
</Entity>

View File

@ -9,5 +9,5 @@
classes="gaia_geo"
/>
</Traits>
<Footprint Radius="1.0"/>
<Footprint Radius="1.0" Height="2.5"/>
</Entity>

View File

@ -13,5 +13,5 @@
type="Special"
/>
</Traits>
<Footprint Radius="1.0"/>
<Footprint Radius="1.0" Height="5.0"/>
</Entity>

View File

@ -16,5 +16,5 @@
</Traits>
<Actions>
</Actions>
<Footprint Radius="1.0"/>
<Footprint Radius="1.0" Height="10.0"/>
</Entity>

View File

@ -31,5 +31,5 @@
construct="1"
/>
</Actions>
<Footprint Width="24.0" Height="24.0"/>
<Footprint Width="24.0" Depth="24.0" Height="8.0"/>
</Entity>

View File

@ -19,5 +19,5 @@
add="5"
/>
</Traits>
<Footprint Width="12.0" Height="12.0"/>
<Footprint Width="12.0" Depth="12.0" Height="8.0"/>
</Entity>

View File

@ -19,5 +19,5 @@
add="5"
/>
</Traits>
<Footprint Width="9.0" Height="9.0"/>
<Footprint Width="9.0" Depth="9.0" Height="8.0"/>
</Entity>

View File

@ -20,5 +20,5 @@
max="60"
/>
</Traits>
<Footprint Width="6.0" Height="6.0"/>
<Footprint Width="6.0" Depth="6.0" Height="8.0"/>
</Entity>

View File

@ -16,5 +16,5 @@
hitpoints="500"
/>
</Traits>
<Footprint Width="6.0" Height="6.0"/>
<Footprint Width="6.0" Depth="6.0" Height="8.0"/>
</Entity>

View File

@ -16,5 +16,5 @@
hitpoints="100"
/>
</Traits>
<Footprint Width="6.0" Height="6.0"/>
<Footprint Width="6.0" Depth="6.0" Height="8.0"/>
</Entity>

View File

@ -20,5 +20,5 @@
max="80"
/>
</Traits>
<Footprint Width="6.0" Height="6.0"/>
<Footprint Width="6.0" Depth="6.0" Height="8.0"/>
</Entity>

View File

@ -20,5 +20,5 @@
max="50"
/>
</Traits>
<Footprint Width="12.0" Height="12.0"/>
<Footprint Width="12.0" Depth="12.0" Height="8.0"/>
</Entity>

View File

@ -16,5 +16,5 @@
hitpoints="50"
/>
</Traits>
<Footprint Width="12.0" Height="12.0"/>
<Footprint Width="12.0" Depth="12.0" Height="8.0"/>
</Entity>

View File

@ -23,5 +23,5 @@
add="10"
/>
</Traits>
<Footprint Width="18.0" Height="18.0"/>
<Footprint Width="18.0" Depth="18.0" Height="8.0"/>
</Entity>

View File

@ -23,5 +23,5 @@
add="20"
/>
</Traits>
<Footprint Width="24.0" Height="24.0"/>
<Footprint Width="24.0" Depth="24.0" Height="8.0"/>
</Entity>

View File

@ -16,5 +16,5 @@
hitpoints="600"
/>
</Traits>
<Footprint Width="18.0" Height="18.0"/>
<Footprint Width="18.0" Depth="18.0" Height="8.0"/>
</Entity>

View File

@ -19,5 +19,5 @@
add="15"
/>
</Traits>
<Footprint Width="18.0" Height="18.0"/>
<Footprint Width="18.0" Depth="18.0" Height="8.0"/>
</Entity>

View File

@ -12,5 +12,5 @@
classes="structure_city"
/>
</Traits>
<Footprint Width="24.0" Height="24.0"/>
<Footprint Width="24.0" Depth="24.0" Height="8.0"/>
</Entity>

View File

@ -30,5 +30,5 @@
los="0"
/>
</Traits>
<Footprint Width="12.0" Height="12.0"/>
<Footprint Width="12.0" Depth="12.0" Height="8.0"/>
</Entity>

View File

@ -30,5 +30,5 @@
los="0"
/>
</Traits>
<Footprint Width="12.0" Height="12.0"/>
<Footprint Width="12.0" Depth="12.0" Height="8.0"/>
</Entity>

View File

@ -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])

View File

@ -32,7 +32,7 @@
los="2"
/>
</Traits>
<Footprint Radius="0.5"/>
<Footprint Radius="0.5" Height="5.0"/>
<Actions>
<Move
speed="6.0"

View File

@ -32,7 +32,7 @@
req="900"
/>
</Traits>
<Footprint Radius="1.5"/>
<Footprint Radius="1.5" Height="7.5"/>
<Actions>
<!-- 2 hack, 2 pierce damage... -->
<Attack

View File

@ -10,4 +10,12 @@
classes="unit_ranged"
/>
</Traits>
<!-- Let's... well... give it some range. -->
<Actions>
<Attack
range="16.0"
/>
</Actions>
<!-- Override this so it uses the ranged attack script, rather than the parent's melee script -->
<Event On="Attack" Function="entity_event_attack_ranged" />
</Entity>

View File

@ -10,4 +10,12 @@
classes="unit_ranged"
/>
</Traits>
<!-- Let's... well... give it some range. -->
<Actions>
<Attack
range="16.0"
/>
</Actions>
<!-- Override this so it uses the ranged attack script, rather than the parent's melee script -->
<Event On="Attack" Function="entity_event_attack_ranged" />
</Entity>

View File

@ -398,7 +398,7 @@
</object>
<object type="input" name="pregame_sp_mapname" sprite="only_black_border" sprite_selectarea="GeeTemp_selected" absolute="false" size="130 90 290 110" textcolor="0 0 0" textcolor_selected="255 255 255">
gathertest
gathertest2
</object>
<object type="button" name="pregame_sp_start_bt" sprite="message_box_button_normal" sprite_over="message_box_button_over" text_align="center" text_valign="center" absolute="false" size="50%-100 100%-50 50%-10 100%-20">

BIN
binaries/data/mods/official/maps/scenarios/gathertest2.pmp (Stored with Git LFS) Normal file

Binary file not shown.

BIN
binaries/data/mods/official/maps/scenarios/gathertest2.xml (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -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 ); } );

View File

@ -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.

View File

@ -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<CProjectile*>& projectiles=pProjectileMan->GetProjectiles();
for (uint i=0;i<projectiles.size();++i) {
if (frustum.IsBoxVisible(CVector3D(0,0,0),projectiles[i]->GetModel()->GetBounds())) {
SubmitModelRecursive(projectiles[i]->GetModel());
}
}
}
void CGameView::SubmitModelRecursive(CModel* model)

View File

@ -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

View File

@ -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;
}

View File

@ -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-<proppoint>'
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);
}
}

View File

@ -3,6 +3,7 @@
class CModel;
class CSkeletonAnim;
struct SPropPoint;
#include <vector>
#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<CObjectBase::Prop> m_Props;

View File

@ -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<CModel::Prop>& props = m_Object->m_Model->GetProps();
std::vector<CModel::Prop>::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 );
}

View File

@ -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;

View File

@ -65,12 +65,14 @@ void CUnitManager::DeleteUnit(CUnit* unit)
// DeleteAll: remove and delete all units
void CUnitManager::DeleteAll()
{
for (uint i=0;i<m_Units.size();i++) {
uint i;
for (i=0;i<m_Units.size();i++) {
delete m_Units[i];
}
m_Units.clear();
}
///////////////////////////////////////////////////////////////////////////////
// PickUnit: iterate through units testing given ray against bounds of each
// unit; return the closest unit, or null if everything missed
@ -120,4 +122,4 @@ CUnit* CUnitManager::CreateUnit(CStr& actorName, CEntity* entity)
CUnit* unit = new CUnit(obj, obj->m_Model->Clone(), entity);
AddUnit(unit);
return unit;
}
}

View File

@ -13,6 +13,7 @@
#include "Singleton.h"
class CUnit;
class CModel;
class CVector3D;
class CEntity;
class CStr;

View File

@ -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<CEntity>::ScriptingInit(); // <-- Doesn't really matter which we use, but we know CJSPropertyAccessor<T> 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();

View File

@ -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<std::wstring>::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)

View File

@ -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();

View File

@ -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;
}

View File

@ -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"

View File

@ -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.

View File

@ -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,

View File

@ -11,7 +11,7 @@
template<> HEntity* ToNative<HEntity>( JSContext* cx, JSObject* obj )
{
CEntity* e = ToNative<CEntity>( cx, obj );
return( &( e->me ) );
return( e ? &( e->me ) : NULL );
}
template<> JSObject* ToScript<HEntity>( HEntity* Native )
@ -22,7 +22,7 @@ template<> JSObject* ToScript<HEntity>( HEntity* Native )
// CPlayer*
template<> bool ToPrimitive<CPlayer*>( 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*>( CPlayer** Native )
template<> bool ToPrimitive<CBaseEntity*>( 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*>( CBaseEntity** Native )
template<> CVector3D* ToNative<CVector3D>( 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>( CVector3D* Native )

View File

@ -23,25 +23,20 @@ class CPlayer;
template<typename T> 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<typename T> JSObject* ToScript( T* Native )
{
if( !Native )
return( (JSObject*)JSVAL_NULL );
return( (JSObject*)NULL );
return( Native->GetScript() );
}
template<typename T> T* ToNative( jsval v )
{
if( !JSVAL_IS_OBJECT( v ) ) return( NULL );
if( v == JSVAL_NULL ) return( NULL );
return( ToNative<T>( g_ScriptingHost.GetContext(), JSVAL_TO_OBJECT( v ) ) );
}

View File

@ -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 );
}

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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

View File

@ -23,6 +23,27 @@ CBoundingObject* getContainingObject( const CVector2D& point )
return( NULL );
}
CEntity* GetCollisionObject( float x, float y )
{
CVector2D point( x, y );
std::vector<HEntity>* entities = g_EntityManager.getExtant();
std::vector<HEntity>::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<HEntity>* 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<HEntity>* entities = g_EntityManager.getExtant();
std::vector<HEntity>::iterator it;
std::vector<CEntity*> entities;
g_EntityManager.GetExtant( entities );
std::vector<CEntity*>::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<CEntity*> entities;
g_EntityManager.GetExtant( entities );
float dist, closestApproach, l;
CVector2D delta;
std::vector<CEntity*>::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 );
}

View File

@ -20,17 +20,24 @@
struct rayIntersectionResults
{
HEntity hEntity;
CEntity* Entity;
CBoundingObject* boundingObject;
CVector2D position;
float closestApproach;
float distance;
};
typedef std::vector<CEntity*> 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

View File

@ -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;

View File

@ -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

View File

@ -1,3 +1,4 @@
#include "precompiled.h"
#include "EntityManager.h"
@ -97,6 +98,14 @@ std::vector<HEntity>* CEntityManager::getExtant()
return( activelist );
}
void CEntityManager::GetExtant( std::vector<CEntity*>& 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 )
{

View File

@ -68,6 +68,7 @@ public:
std::vector<HEntity>* matches( EntityPredicate predicate, void* userdata = NULL );
std::vector<HEntity>* getExtant();
void GetExtant( std::vector<CEntity*>& 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 );

View File

@ -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 )

View File

@ -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<CProjectile*>::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<CProjectile>::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<CEntity>( 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<CStr>( cx, argv[0], ModelString ) || !( oe = g_ObjMan.FindObject( ModelString ) ) || !( Model = oe->m_Model ) )
{
err = "Invalid actor";
goto fail;
}
if( Temp = ToNative<CEntity>( 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<CVector3D>( cx, argv[1], Here ) ) )
{
err = "Invalid vector";
goto fail;
}
if( Temp = ToNative<CEntity>( 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<CVector3D>( cx, argv[2], There ) ) )
{
err = "Invalid vector";
goto fail;
}
if( ( Speed = ToPrimitive<float>( 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<CEntity>( 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<CProjectile>( *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<CProjectile*>::iterator it;
for( it = m_Projectiles.begin(); it != m_Projectiles.end(); ++it )
(*it)->Interpolate( absoluteOffset );
}

View File

@ -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<CProjectile>, public IEventTarget
{
friend class CProjectileManager;
friend class CJSObject<CProjectile>;
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<CProjectileManager>
{
friend class CProjectile;
public:
CProjectileManager();
~CProjectileManager();
void UpdateAll( size_t timestep );
void InterpolateAll( double frametime );
inline const std::vector<CProjectile*>& 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<CProjectile*> m_Projectiles;
};
#define g_ProjectileManager CProjectileManager::GetSingleton()
#endif

View File

@ -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();

View File

@ -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();

View File

@ -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;i<units.size();++i)
units[i]->GetModel()->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);