1
0
forked from 0ad/0ad

#Fixes to flank penalty, notifications, sectors, terrain conformance. Added speed bonus based on terrain slope.

-added NOTIFY_ORDER_CHANGE, which is used for flank penalty instead of
idle.
-entity speed is now actions.move.speed_curr for the current speed, but
the original speed is still actions.move.speed.  Changes take place in
entityEventMovemen.

This was SVN commit r3840.
This commit is contained in:
pyrolink 2006-05-04 04:14:48 +00:00
parent 16b94e5604
commit 39e89c406e
27 changed files with 444 additions and 277 deletions

View File

@ -24,8 +24,8 @@
; System settings: ; System settings:
novbo=false novbo=false
noframebufferobject=false noframebufferobject=true
shadows=true shadows=false
vsync=false vsync=false
; Specify the render path. This can be one of: ; Specify the render path. This can be one of:
@ -34,7 +34,7 @@ vsync=false
; vertexshader Use vertex shaders for transform and lighting where possible ; vertexshader Use vertex shaders for transform and lighting where possible
; Using 'fixed' instead of 'default' may work around some graphics-related problems, ; Using 'fixed' instead of 'default' may work around some graphics-related problems,
; but will reduce performance when a modern graphics card is available. ; but will reduce performance when a modern graphics card is available.
renderpath=default renderpath=fixed
; Adjusts how OpenGL calculates mipmap level of detail. 0.0f is the default (blurry) value. ; Adjusts how OpenGL calculates mipmap level of detail. 0.0f is the default (blurry) value.
; Lower values sharpen/extend, and higher values blur/decrease. Clamped at -3.0 to 3.0. ; Lower values sharpen/extend, and higher values blur/decrease. Clamped at -3.0 to 3.0.

View File

@ -1,6 +1,7 @@
<formation Tag="Form Box 2" <formation Tag="Form Box 2"
bonus="actions.move.speed" bonus="actions.move.speed_curr"
bonusbase="actions.move.speed"
bonustype="Melee" bonustype="Melee"
bonusval=".2" bonusval=".2"
penalty="" penalty=""

View File

@ -13,6 +13,9 @@
<!-- All but sea units are attached to the ground plane. --> <!-- All but sea units are attached to the ground plane. -->
<Anchor> <Anchor>
<Type>Ground</Type> <Type>Ground</Type>
<!-- If negative or > 90.0, entity will always conform to terrain (assuming traits.pitch allows it) -->
<ConformX>3.141592</ConformX>
<ConformZ>3.141592</ConformZ>
</Anchor> </Anchor>
<!-- Defaults to no armour. --> <!-- Defaults to no armour. -->
@ -49,14 +52,13 @@
<Height>-1.0</Height> <Height>-1.0</Height>
<Name></Name> <Name></Name>
</Rank> </Rank>
<AnglePenalty> <Flank_Penalty>
<Sectors>6</Sectors> <Sectors>6</Sectors>
<Value>.2</Value> <Value>.2</Value>
</AnglePenalty> </Flank_Penalty>
<Pitch> <Pitch>
<Max_Actor>0.03</Max_Actor> <Sectors>7</Sectors>
<Min_Actor>-0.03</Min_Actor> <Value>.1</Value>
</Pitch> </Pitch>
</Traits> </Traits>

View File

@ -22,5 +22,6 @@
<Event On="Notification" Function="entityEventNotification" /> <Event On="Notification" Function="entityEventNotification" />
<Event On="Formation" Function="entityEventFormation" /> <Event On="Formation" Function="entityEventFormation" />
<Event On="Idle" Function="entityEventIdle" /> <Event On="Idle" Function="entityEventIdle" />
<Event On="Movement" Function="entityEventMovement" />
</Entity> </Entity>

View File

@ -157,6 +157,9 @@ function entityInit()
// Register our actions with the generic order system // Register our actions with the generic order system
if( this.actions ) if( this.actions )
{ {
if ( this.actions.move && this.actions.move.speed )
this.actions.move.speed_curr = this.actions.move.speed;
if( this.actions.attack && this.actions.attack.melee ) if( this.actions.attack && this.actions.attack.melee )
{ {
a = this.actions.attack.melee; a = this.actions.attack.melee;
@ -341,7 +344,7 @@ function performAttack( evt )
// Attack logic. // Attack logic.
var dmg = new DamageType(); var dmg = new DamageType();
var flank = (evt.target.getAttackDirections()-1)*evt.target.traits.flank_penalty.value;
if ( this.getRunState() ) if ( this.getRunState() )
{ {
dmg.crush = parseInt(this.actions.attack.charge.damage * this.actions.attack.charge.crush); dmg.crush = parseInt(this.actions.attack.charge.damage * this.actions.attack.charge.crush);
@ -354,6 +357,9 @@ function performAttack( evt )
dmg.hack = parseInt(this.actions.attack.melee.damage * this.actions.attack.melee.hack); dmg.hack = parseInt(this.actions.attack.melee.damage * this.actions.attack.melee.hack);
dmg.pierce = parseInt(this.actions.attack.melee.damage * this.actions.attack.melee.pierce); dmg.pierce = parseInt(this.actions.attack.melee.damage * this.actions.attack.melee.pierce);
} }
dmg.crush += dmg.crush * flank;
dmg.hack += dmg.hack * flank;
dmg.pierce += dmg.pierce * flank;
evt.target.damage( dmg, this ); evt.target.damage( dmg, this );
@ -371,7 +377,12 @@ function performAttackRanged( evt )
dmg.hack = parseInt(this.actions.attack.ranged.damage * this.actions.attack.ranged.hack); dmg.hack = parseInt(this.actions.attack.ranged.damage * this.actions.attack.ranged.hack);
dmg.pierce = parseInt(this.actions.attack.ranged.damage * this.actions.attack.ranged.pierce); dmg.pierce = parseInt(this.actions.attack.ranged.damage * this.actions.attack.ranged.pierce);
// The parameters for Projectile are: var flank = (evt.target.getAttackDirections()-1)*evt.target.traits.flank_penalty.value;
dmg.crush += dmg.crush * flank;
dmg.hack += dmg.hack * flank;
dmg.pierce += dmg.pierce * flank;
// The parameters for Projectile are:
// 1 - The actor to use as the projectile. There are two ways of specifying this: // 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 // 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 // in the actor of that entity. This way is usual, and preferred - visual
@ -564,7 +575,7 @@ function damage( dmg, inflictor )
if(!this.traits.armour) return; // corpses have no armour, everything else should if(!this.traits.armour) return; // corpses have no armour, everything else should
this.last_combat_time = getGameTime(); this.last_combat_time = getGameTime();
// Apply armour and work out how much damage we actually take // Apply armour and work out how much damage we actually take
crushDamage = parseInt(dmg.crush - this.traits.armour.value * this.traits.armour.crush); crushDamage = parseInt(dmg.crush - this.traits.armour.value * this.traits.armour.crush);
if ( crushDamage < 0 ) crushDamage = 0; if ( crushDamage < 0 ) crushDamage = 0;
@ -666,14 +677,14 @@ function damage( dmg, inflictor )
// If we're not already doing something else, take a measured response - hit 'em back. // If we're not already doing something else, take a measured response - hit 'em back.
// You know, I think this is quite possibly the first AI code the AI divlead has written // You know, I think this is quite possibly the first AI code the AI divlead has written
// for 0 A.D.... // for 0 A.D....
//When the entity changes order, we can readjust flank penalty. We must destroy the notifiers ourselves later,however.
this.requestNotification( inflictor, NOTIFY_ORDER_CHANGE, false, true );
this.registerDamage( inflictor );
if( this.isIdle() ) if( this.isIdle() )
this.order( ORDER_GENERIC, inflictor, getAttackAction( this, inflictor ) ); this.order( ORDER_GENERIC, inflictor, getAttackAction( this, inflictor ) );
} }
// FIXME: These seemed to cause a crash I fixed the spelling from register to Register, so I've commented them out (Matei)
//When the entity is idle, we can readjust angle penalty. We must destroy the notifiers ourselves later, however.
//this.requestNotification( inflictor, NOTIFY_IDLE, false, true );
//this.registerDamage( inflictor );
} }
// ==================================================================== // ====================================================================
@ -710,11 +721,18 @@ function entityEventGeneric( evt )
function entityEventNotification( evt ) function entityEventNotification( evt )
{ {
//This is used to adjust the flank penalty (we're no longer being attacked).
if ( this.getCurrentRequest() == NOTIFY_ORDER_CHANGE )
{
this.registerOrderChange();
destroyNotifier( evt.target );
return;
}
//Add "true" to the end of order() to indicate that this is a notification order. //Add "true" to the end of order() to indicate that this is a notification order.
switch( evt.notifyType ) switch( evt.notifyType )
{ {
case NOTIFY_GOTO: case NOTIFY_GOTO:
this.GotoInRange( evt.location.x, evt.location.z, false); this.GotoInRange( evt.location.x, evt.location.z, false);
break; break;
case NOTIFY_RUN: case NOTIFY_RUN:
@ -731,13 +749,13 @@ function entityEventNotification( evt )
this.order( ORDER_GENERIC, evt.target, ACTION_GATHER, true ); this.order( ORDER_GENERIC, evt.target, ACTION_GATHER, true );
break; break;
case NOTIFY_IDLE: case NOTIFY_IDLE:
//target is the unit that has become idle //target is the unit that has become idle. Eventually...do something here.
this.registerIdle( evt.target );
break; break;
default: default:
console.write( "Unknown notification request " + evt.notifyType ); console.write( "Unknown notification request " + evt.notifyType );
break; return;
} }
} }
// ==================================================================== // ====================================================================
@ -765,9 +783,30 @@ function entityComplete()
//===================================================================== //=====================================================================
function entityEventIdle( evt ) function entityEventIdle( evt )
{ {
//Use our own data for target; we aren't affecting anyone, so listeners wants to know about us //Use our own data for target; we aren't affecting anyone, so listeners want to know about us
this.forceCheckListeners( NOTIFY_IDLE, this ); this.forceCheckListeners( NOTIFY_IDLE, this );
} }
//=====================================================================
function entityEventMovement( evt )
{
var divs = this.traits.pitch.sectors;
var sector = this.findSector( divs, evt.slope, 3.141592, true );
if ( divs % 2 )
{
sector += ((divs+1)/2 - sector)*2;
sector -= (divs+1)/2;
this.actions.move.speed_curr = this.actions.move.speed;
this.actions.move.speed_curr += this.actions.move.speed*this.traits.pitch.value*sector;
}
else
{
sector += ((divs)/2 - sector)*2;
sector -= (divs)/2;
this.actions.move.speed_curr = this.actions.move.speed;
this.actions.move.speed_curr += this.actions.move.speed*this.traits.pitch.value*sector;
}
}
// ==================================================================== // ====================================================================
@ -867,26 +906,40 @@ function entityEventPrepareOrder( evt )
//evt.notifySource is the entity order data will be obtained from, so if we're attacking and we //evt.notifySource is the entity order data will be obtained from, so if we're attacking and we
//want our listeners to copy us, then we will use our own order as the source. //want our listeners to copy us, then we will use our own order as the source.
//registerOrderChange() is used to adjust the flank penalty
switch( evt.orderType ) switch( evt.orderType )
{ {
case ORDER_GOTO: case ORDER_GOTO:
if ( !this.actions.move ) if ( !this.actions.move )
{
evt.preventDefault(); evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_GOTO; evt.notifyType = NOTIFY_GOTO;
evt.notifySource = this; evt.notifySource = this;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break; break;
case ORDER_RUN: case ORDER_RUN:
if ( !this.actions.move.run ) if ( !this.actions.move.run )
{
evt.preventDefault(); evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_RUN; evt.notifyType = NOTIFY_RUN;
evt.notifySource = this; evt.notifySource = this;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break; break;
case ORDER_PATROL: case ORDER_PATROL:
if ( !this.actions.patrol ) if ( !this.actions.patrol )
{
evt.preventDefault(); evt.preventDefault();
return;
}
this.registerOrderChange();
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break; break;
case ORDER_GENERIC: case ORDER_GENERIC:
@ -897,26 +950,42 @@ function entityEventPrepareOrder( evt )
case ACTION_ATTACK_RANGED: case ACTION_ATTACK_RANGED:
evt.action = getAttackAction( this, evt.target ); evt.action = getAttackAction( this, evt.target );
if ( evt.action == ACTION_NONE ) if ( evt.action == ACTION_NONE )
evt.preventDefault(); {
evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_ATTACK; evt.notifyType = NOTIFY_ATTACK;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break; break;
case ACTION_GATHER: case ACTION_GATHER:
if ( !this.actions.gather ) if ( !this.actions.gather )
{
evt.preventDefault(); evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_GATHER; evt.notifyType = NOTIFY_GATHER;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break; break;
case ACTION_HEAL: case ACTION_HEAL:
if ( !this.actions.heal ) if ( !this.actions.heal )
{
evt.preventDefault(); evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_HEAL; evt.notifyType = NOTIFY_HEAL;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break; break;
case ACTION_BUILD: case ACTION_BUILD:
if ( !this.actions.build ) if ( !this.actions.build )
{
evt.preventDefault(); evt.preventDefault();
return;
}
evt.notifyType = NOTIFY_NONE; evt.notifyType = NOTIFY_NONE;
this.forceCheckListeners( NOTIFY_ORDER_CHANGE, this );
break; break;
} }
break; break;
@ -1463,12 +1532,12 @@ function entityEventFormation( evt )
{ {
if ( this.getFormationBonus() && this.hasClass( this.getFormationBonusType() ) ) if ( this.getFormationBonus() && this.hasClass( this.getFormationBonusType() ) )
{ {
eval( this + this.getFormationBonus() ) += eval( this + this.getFormationBonus() ) * eval( this + this.getFormationBonus() ) += eval( this + this.getFormationBonusBase() ) *
this.getFormationBonusVal(); this.getFormationBonusVal();
} }
if ( this.getFormationPenalty() && this.hasInClass( this.getFormationPenaltyType() ) ) if ( this.getFormationPenalty() && this.hasInClass( this.getFormationPenaltyType() ) )
{ {
eval( this + this.getFormationPenalty() ) -= eval( this + this.getFormationbonus() ) * eval( this + this.getFormationPenalty() ) -= eval( this + this.getFormationPenaltyBase() ) *
this.getFormationPenaltyVal(); this.getFormationPenaltyVal();
} }
} }
@ -1477,12 +1546,12 @@ function entityEventFormation( evt )
{ {
if ( this.getFormationPenalty() && this.hasInClass( this.getFormationPenaltyType() ) ) if ( this.getFormationPenalty() && this.hasInClass( this.getFormationPenaltyType() ) )
{ {
eval( this + this.getFormationPenalty() ) += eval( this + this.getFormationbonus() ) * eval( this + this.getFormationPenalty() ) += eval( this + this.getFormationPenaltyBase() ) *
this.getFormationPenaltyVal(); this.getFormationPenaltyVal();
} }
if ( this.getFormationBonus() && this.hasClass( this.getFormationBonusType() ) ) if ( this.getFormationBonus() && this.hasClass( this.getFormationBonusType() ) )
{ {
eval( this + this.getFormationBonus() ) -= eval( this + this.getFormationBonus() ) * this.getFormationBonusVal(); eval( this + this.getFormationBonus() ) -= eval( this + this.getFormationBonusBase() ) * this.getFormationBonusVal();
} }
} }

View File

@ -77,6 +77,7 @@
<Move> <Move>
<Speed>7.0</Speed> <Speed>7.0</Speed>
<Speed_Curr/>
<TurningRadius>0.0</TurningRadius> <TurningRadius>0.0</TurningRadius>
<Run> <Run>

View File

@ -510,7 +510,7 @@ void CGameView::Update(float DeltaTime)
if (m_UnitView) if (m_UnitView)
{ {
m_ViewCamera.m_Orientation.SetYRotation(m_UnitView->m_orientation); m_ViewCamera.m_Orientation.SetYRotation(m_UnitView->m_orientation.Y);
m_ViewCamera.m_Orientation.Translate(m_UnitViewProp->GetTransform().GetTranslation()); m_ViewCamera.m_Orientation.Translate(m_UnitViewProp->GetTransform().GetTranslation());
m_ViewCamera.UpdateFrustum(); m_ViewCamera.UpdateFrustum();
return; return;

View File

@ -179,7 +179,7 @@ int CMapReader::ApplyData()
{ {
// initialise the terrain // initialise the terrain
pTerrain->Initialize(m_MapSize, &m_Heightmap[0]); pTerrain->Initialize(m_MapSize, &m_Heightmap[0]);
// setup the textures on the minipatches // setup the textures on the minipatches
STileDesc* tileptr = &m_Tiles[0]; STileDesc* tileptr = &m_Tiles[0];
for (u32 j=0; j<m_MapSize; j++) { for (u32 j=0; j<m_MapSize; j++) {
@ -218,13 +218,14 @@ int CMapReader::ApplyData()
unit->GetModel()->SetTransform(transform); unit->GetModel()->SetTransform(transform);
} }
} }
//Make units start out conforming correctly
g_EntityManager.conformAll();
if (unpacker.GetVersion() >= 2) if (unpacker.GetVersion() >= 2)
{ {
// copy over the lighting parameters // copy over the lighting parameters
*pLightEnv = m_LightEnv; *pLightEnv = m_LightEnv;
} }
return 0; return 0;
} }

View File

@ -254,7 +254,7 @@ void CMapWriter::WriteXML(const char* filename, CUnitManager* pUnitMan, CLightEn
} }
{ {
float angle = entity->m_orientation; float angle = entity->m_orientation.Y;
XML_Element("Orientation"); XML_Element("Orientation");
XML_Attribute("angle", angle); XML_Attribute("angle", angle);

View File

@ -14,6 +14,8 @@
#include "renderer/Renderer.h" #include "renderer/Renderer.h"
#include "renderer/WaterManager.h" #include "renderer/WaterManager.h"
#include "EntityManager.h"
#include <string.h> #include <string.h>
#include "Terrain.h" #include "Terrain.h"
#include "MathUtil.h" #include "MathUtil.h"
@ -207,14 +209,27 @@ float CTerrain::getSlope(float x, float z) const
return MAX(MAX(h00, h01), MAX(h10, h11)) - return MAX(MAX(h00, h01), MAX(h10, h11)) -
MIN(MIN(h00, h01), MIN(h10, h11)); MIN(MIN(h00, h01), MIN(h10, h11));
} }
float CTerrain::getSlopeAngle( float x, float y ) const CVector2D CTerrain::getSlopeAngleFace(float x, float y, CEntity*& entity ) const
{ {
//side start at 0 at -180 degrees, and end up at 7 at 180 degrees
int side;
bool invert;
float top, bottom, topZ, bottomZ;
float fCell = (float)CELL_SIZE; float fCell = (float)CELL_SIZE;
x /= fCell; x /= fCell;
y /= fCell; y /= fCell;
int xi = (int)floor(x); int xi = (int)floor(x);
int yi = (int)floor(y); int yi = (int)floor(y);
CVector3D flat(0.0f, 0.0f, 0.0f);
CVector3D flatZ = flat;
CVector2D ret;
side = entity->findSector( 8, entity->m_orientation.Y, DEGTORAD(360.0f) ) - 1;
//Wrap around
if ( side < 0 )
side += 8;
//Keep it in bounds //Keep it in bounds
if (xi < 0) if (xi < 0)
xi = 0; xi = 0;
@ -230,125 +245,45 @@ float CTerrain::getSlopeAngle( float x, float y ) const
float h10 = m_Heightmap[yi*m_MapSize + xi + 1] * HEIGHT_SCALE; float h10 = m_Heightmap[yi*m_MapSize + xi + 1] * HEIGHT_SCALE;
float h11 = m_Heightmap[yi*m_MapSize + xi + m_MapSize + 1] * HEIGHT_SCALE; float h11 = m_Heightmap[yi*m_MapSize + xi + m_MapSize + 1] * HEIGHT_SCALE;
CVector3D flat, elevated; //Find heights
float low, high; if ( side == 0 || side == 7 )
if ( h00 < h01 && h00 < h10 && h00 < h11 )
low = h00;
else if ( h01 < h10 && h01 < h11 )
low = h01;
else if ( h10 < h11 )
low = h10;
else
low = h11;
//Find correct vector representing the flat version of the vector from low to high points
if ( h00 > h01 && h00 > h10 && h00 > h11 )
{ {
high = h00; top = (h00 + h10)/2.0f;
if ( low == h10 ) bottom = (h01 + h11)/2.0f;
flat = CVector3D( fCell, 0.0f, 0.0f ); topZ = (h00 + h01)/2.0f;
else if ( low == h01 ) bottomZ = (h10 + h11)/2.0f;
flat = CVector3D( 0.0f, 0.0f, fCell ); flat.Z = sqrtf(SQR(CELL_SIZE) - SQR(top-bottom));
else if ( low == h11 ) flatZ.X = sqrtf(SQR(CELL_SIZE) - SQR(topZ-bottomZ));
flat = CVector3D( fCell, 0.0f, fCell );
} }
else if ( h01 > h10 && h01 > h11 ) else if ( side == 1 || side == 2 )
{ {
high = h01; top = (h00 + h01)/2.0f;
if ( low == h00 ) bottom = (h10 + h11)/2.0f;
flat = CVector3D( 0.0f, 0.0f, fCell ); topZ = (h01 + h11)/2.0f;
else if ( low == h01 ) bottomZ = (h00 + h10)/2.0f;
flat = CVector3D( fCell, 0.0f, fCell ); flat.X = sqrtf(SQR(CELL_SIZE) - SQR(top-bottom));
else if ( low == h11 ) flatZ.Z = sqrtf(SQR(CELL_SIZE) - SQR(topZ-bottomZ));
flat = CVector3D( fCell, 0.0f, 0.0f );
} }
else if ( h10 > h11 ) else if ( side == 3 || side == 4 )
{ {
high = h10; top = (h01 + h11)/2.0f;
if ( low == h00 ) bottom = (h00 + h10)/2.0f;
flat = CVector3D( fCell, 0.0f, 0.0f ); topZ = (h11 + h10)/2.0f;
else if ( low == h01 ) bottomZ = (h00 + h01)/2.0f;
flat = CVector3D( fCell, 0.0f, fCell ); flat.Z = sqrtf(SQR(CELL_SIZE) - SQR(top-bottom));
else if ( low == h11 ) flatZ.X = sqrtf(SQR(CELL_SIZE) - SQR(topZ-bottomZ));
flat = CVector3D( 0.0f, 0.0f, fCell );
} }
else else
{ {
high = h11; top = (h11 + h10)/2.0f;
if ( low == h10 ) bottom = (h00 + h01)/2.0f;
flat = CVector3D( 0.0f, 0.0f, fCell ); topZ = (h00 + h10)/2.0f;
else if ( low == h01 ) bottomZ = (h01 + h11)/2.0f;
flat = CVector3D( fCell, 0.0f, 0.0f ); flat.X = sqrtf(SQR(CELL_SIZE) - SQR(top-bottom));
else if ( low == h00 ) flatZ.Z = sqrtf(SQR(CELL_SIZE) - SQR(topZ-bottomZ));
flat = CVector3D( fCell, 0.0f, fCell );
} }
elevated = flat;
elevated.Y = high - low;
elevated.Normalize();
flat.Normalize();
return acosf( flat.Dot( elevated ) );
}
float CTerrain::getSlopeAngleFace(float x, float y, float orientation) const
{
bool right; //true means use 0,0 and 1,1; false means use 1,0 and 0,1
bool invert;
float top, bottom;
x /= (float)CELL_SIZE;
y /= (float)CELL_SIZE;
int xi = (int)floor(x);
int yi = (int)floor(y);
CVector3D flat( (float)CELL_SIZE, 0.0f, (float)CELL_SIZE );
CVector3D elevated=flat; CVector3D elevated=flat;
elevated.Y = top-bottom;
/* JW: currently all unused, so commented out to avoid warning
const float a0 = DEGTORAD(0.0f);
const float a90 = DEGTORAD(90.0f);
const float a180 = DEGTORAD(180.0f);
const float neg = DEGTORAD(-90.0f);
const float a45 = DEGTORAD(45.0f);
const float a135 = DEGTORAD(135.0f);*/
//Find which side it's facing; use that and the opposite
if ( orientation > 0.0f && orientation < DEGTORAD(90.0f) )
right = true;
else if ( orientation > DEGTORAD(90.0f) && orientation < DEGTORAD(180.0f) )
right = false;
else if ( orientation < DEGTORAD(-180.0f) && orientation > DEGTORAD(-90.0f) )
right = true;
else
right = false;
//Keep it in bounds
if (xi < 0)
xi = 0;
else if (xi >= (int)m_MapSize-1)
xi = m_MapSize - 2;
if (yi < 0)
yi = 0;
else if (yi >= (int)m_MapSize-1)
yi = m_MapSize - 2;
if ( right )
{
bottom = m_Heightmap[yi*m_MapSize + xi]*HEIGHT_SCALE;
top = m_Heightmap[yi*m_MapSize+m_MapSize + xi + 1]*HEIGHT_SCALE;
if ( (orientation > DEGTORAD(-45.0f) && orientation < 0.0f) ||
(orientation < DEGTORAD(135.0f) && orientation > 0.0f) )
elevated.Y = top-bottom;
else
elevated.Y = bottom-top;
}
else
{
bottom = m_Heightmap[yi*m_MapSize + xi + 1]*HEIGHT_SCALE;
top = m_Heightmap[yi*m_MapSize+m_MapSize + xi]*HEIGHT_SCALE;
if ( (orientation > DEGTORAD(-135.0f) && orientation < 0.0f) ||
(orientation < DEGTORAD(45.0f) && orientation > 0.0f) )
elevated.Y = top-bottom;
else
elevated.Y = bottom-top;
}
if ( elevated.Y > 0.0f ) if ( elevated.Y > 0.0f )
invert=false; invert=false;
else else
@ -356,12 +291,27 @@ float CTerrain::getSlopeAngleFace(float x, float y, float orientation) const
elevated.Y = fabs(elevated.Y); elevated.Y = fabs(elevated.Y);
elevated.Normalize(); elevated.Normalize();
flat.Normalize(); flat.Normalize();
float ret = elevated.Dot(flat); if ( invert )
ret.x = -acosf( elevated.Dot(flat) );
else
ret.x = acosf( elevated.Dot(flat) );
if (invert) //Z component
return -acosf(ret); elevated = flatZ;
return acosf(ret); elevated.Y = topZ-bottomZ;
if ( elevated.Y > 0.0f )
invert=true;
else
invert=false;
elevated.Y = fabs(elevated.Y);
elevated.Normalize();
flatZ.Normalize();
if ( invert )
ret.y = -acosf( elevated.Dot(flatZ) );
else
ret.y = acosf( elevated.Dot(flatZ) );
return ret;
} }
float CTerrain::getExactGroundLevel(float x, float z) const float CTerrain::getExactGroundLevel(float x, float z) const

View File

@ -13,6 +13,7 @@
#include "Patch.h" #include "Patch.h"
#include "Vector3D.h" #include "Vector3D.h"
#include "Vector2D.h" #include "Vector2D.h"
#include "Entity.h"
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// CTerrain: main terrain class; contains the heightmap describing elevation // CTerrain: main terrain class; contains the heightmap describing elevation
@ -45,9 +46,8 @@ public:
inline float getExactGroundLevel(const CVector2D& v) const { return getExactGroundLevel(v.x, v.y); } inline float getExactGroundLevel(const CVector2D& v) const { return getExactGroundLevel(v.x, v.y); }
float getSlope(float x, float z) const ; float getSlope(float x, float z) const ;
float getSlopeAngle( float x, float y) const; //In radians //Find the slope of in X and Z axes depending on the way the entity is facing
//Same as above, but picks the two vertices that the unit is facing (front+back) for slope CVector2D getSlopeAngleFace(float x, float y, CEntity*& entity) const;
float getSlopeAngleFace(float x, float y, float orientation) const;
// resize this terrain such that each side has given number of patches // resize this terrain such that each side has given number of patches
void Resize(u32 size); void Resize(u32 size);

View File

@ -73,7 +73,6 @@ PSRETURN CGame::RegisterInit(CGameAttributes* pAttribs)
m_GameView->RegisterInit(pAttribs); m_GameView->RegisterInit(pAttribs);
m_World->RegisterInit(pAttribs); m_World->RegisterInit(pAttribs);
m_Simulation->RegisterInit(pAttribs); m_Simulation->RegisterInit(pAttribs);
LDR_EndRegistering(); LDR_EndRegistering();
return 0; return 0;
} }

View File

@ -522,6 +522,7 @@ static void InitScripting()
g_ScriptingHost.DefineConstant( "NOTIFY_HEAL", CEntityListener::NOTIFY_HEAL ); g_ScriptingHost.DefineConstant( "NOTIFY_HEAL", CEntityListener::NOTIFY_HEAL );
g_ScriptingHost.DefineConstant( "NOTIFY_GATHER", CEntityListener::NOTIFY_GATHER ); g_ScriptingHost.DefineConstant( "NOTIFY_GATHER", CEntityListener::NOTIFY_GATHER );
g_ScriptingHost.DefineConstant( "NOTIFY_IDLE", CEntityListener::NOTIFY_IDLE ); g_ScriptingHost.DefineConstant( "NOTIFY_IDLE", CEntityListener::NOTIFY_IDLE );
g_ScriptingHost.DefineConstant( "NOTIFY_ORDER_CHANGE", CEntityListener::NOTIFY_ORDER_CHANGE );
g_ScriptingHost.DefineConstant( "NOTIFY_ALL", CEntityListener::NOTIFY_ALL ); g_ScriptingHost.DefineConstant( "NOTIFY_ALL", CEntityListener::NOTIFY_ALL );
g_ScriptingHost.DefineConstant( "ORDER_NONE", -1 ); g_ScriptingHost.DefineConstant( "ORDER_NONE", -1 );

View File

@ -23,6 +23,7 @@ enum EEventType
EVENT_NOTIFICATION, EVENT_NOTIFICATION,
EVENT_FORMATION, EVENT_FORMATION,
EVENT_IDLE, EVENT_IDLE,
EVENT_MOVEMENT,
EVENT_LAST, EVENT_LAST,
// Projectile events // Projectile events
@ -51,7 +52,9 @@ static const wchar_t* const EventNames[EVENT_LAST] =
/* EVENT_ORDER_TRANSITION */ L"onOrderTransition", /* When we change orders (sometimes...) */ /* EVENT_ORDER_TRANSITION */ L"onOrderTransition", /* When we change orders (sometimes...) */
/* EVENT_NOTIFICATION */ L"onNotification", /*When we receive a notification */ /* EVENT_NOTIFICATION */ L"onNotification", /*When we receive a notification */
/* EVENT_FORMATION */ L"onFormation", /* When this unit does something with a formation */ /* EVENT_FORMATION */ L"onFormation", /* When this unit does something with a formation */
/* EVENT_IDLE */ L"onIdle" /* When this unit becomes idle, do something */ /* EVENT_IDLE */ L"onIdle", /* When this unit becomes idle, do something */
/* EVENT_MOVEMENT */ L"onMovement" /*Triggered by processGotoHelper(), when unit moves */
}; };
#endif // #ifndef EVENTTYPES_H__ #endif // #ifndef EVENTTYPES_H__

View File

@ -17,9 +17,9 @@ CBaseEntity::CBaseEntity()
AddProperty( L"tag", &m_Tag, false ); AddProperty( L"tag", &m_Tag, false );
AddProperty( L"parent", &m_base, false ); AddProperty( L"parent", &m_base, false );
AddProperty( L"actions.move.speed", &m_speed ); AddProperty( L"actions.move.speed_curr", &m_speed );
AddProperty( L"actions.move.turningradius", &m_turningRadius ); AddProperty( L"actions.move.turningradius", &m_turningRadius );
AddProperty( L"actions.move.run.speed", &( m_run.m_Speed ) ); AddProperty( L"actions.move.run.speed.curr", &( m_run.m_Speed ) );
AddProperty( L"actions.move.run.rangemin", &( m_run.m_MinRange ) ); AddProperty( L"actions.move.run.rangemin", &( m_run.m_MinRange ) );
AddProperty( L"actions.move.run.range", &( m_run.m_MaxRange ) ); AddProperty( L"actions.move.run.range", &( m_run.m_MaxRange ) );
AddProperty( L"actions.move.run.regen_rate", &m_runRegenRate ); AddProperty( L"actions.move.run.regen_rate", &m_runRegenRate );
@ -47,10 +47,8 @@ CBaseEntity::CBaseEntity()
AddProperty( L"traits.stamina.border_height", &m_staminaBorderHeight); AddProperty( L"traits.stamina.border_height", &m_staminaBorderHeight);
AddProperty( L"traits.stamina.border_width", &m_staminaBorderWidth ); AddProperty( L"traits.stamina.border_width", &m_staminaBorderWidth );
AddProperty( L"traits.stamina.border_name", &m_staminaBorderName ); AddProperty( L"traits.stamina.border_name", &m_staminaBorderName );
AddProperty( L"traits.angle_penalty.sectors", &m_sectorDivs ); AddProperty( L"traits.flank_penalty.sectors", &m_sectorDivs );
AddProperty( L"traits.angle_penalty.value", &m_sectorPenalty ); AddProperty( L"traits.pitch.sectors", &m_pitchDivs );
AddProperty( L"traits.pitch.max_actor", &m_maxActorPitch );
AddProperty( L"traits.pitch.min_actor", &m_minActorPitch );
AddProperty( L"traits.rank.width", &m_rankWidth ); AddProperty( L"traits.rank.width", &m_rankWidth );
AddProperty( L"traits.rank.height", &m_rankHeight ); AddProperty( L"traits.rank.height", &m_rankHeight );
AddProperty( L"traits.rank.name", &m_rankName ); AddProperty( L"traits.rank.name", &m_rankName );
@ -59,6 +57,8 @@ CBaseEntity::CBaseEntity()
AddProperty( L"traits.minimap.green", &m_minimapG ); AddProperty( L"traits.minimap.green", &m_minimapG );
AddProperty( L"traits.minimap.blue", &m_minimapB ); AddProperty( L"traits.minimap.blue", &m_minimapB );
AddProperty( L"traits.anchor.type", &m_anchorType ); AddProperty( L"traits.anchor.type", &m_anchorType );
AddProperty( L"traits.anchor.conformx", &m_anchorConformX );
AddProperty( L"traits.anchor.conformz", &m_anchorConformZ );
AddProperty( L"traits.vision.los", &m_los ); AddProperty( L"traits.vision.los", &m_los );
AddProperty( L"traits.vision.permanent", &m_permanent ); AddProperty( L"traits.vision.permanent", &m_permanent );
AddProperty( L"traits.foundation", &m_foundation ); AddProperty( L"traits.foundation", &m_foundation );
@ -76,6 +76,7 @@ CBaseEntity::CBaseEntity()
m_foundation = CStrW(); m_foundation = CStrW();
// Sentinel values for stamina and health (so scripts can check if an entity has no stamina or no HP). // Sentinel values for stamina and health (so scripts can check if an entity has no stamina or no HP).
m_speed=0;
m_staminaCurr = 0; m_staminaCurr = 0;
m_staminaMax = 0; m_staminaMax = 0;
m_healthCurr = 0; m_healthCurr = 0;

View File

@ -111,10 +111,9 @@ public:
SEntityAction m_generic; SEntityAction m_generic;
int m_sectorDivs; int m_sectorDivs;
float m_sectorPenalty; int m_pitchDivs;
float m_anchorConformX;
float m_maxActorPitch; float m_anchorConformZ;
float m_minActorPitch;
float m_turningRadius; float m_turningRadius;
CScriptObject m_EventHandlers[EVENT_LAST]; CScriptObject m_EventHandlers[EVENT_LAST];

View File

@ -26,9 +26,11 @@ bool CBaseFormation::loadXML(CStr filename)
AT(tag); AT(tag);
AT(bonus); AT(bonus);
AT(bonusbase);
AT(bonustype); AT(bonustype);
AT(bonusval); AT(bonusval);
AT(penalty); AT(penalty);
AT(penaltybase);
AT(penaltytype); AT(penaltytype);
AT(penaltyval); AT(penaltyval);
AT(anglepenalty); AT(anglepenalty);
@ -63,12 +65,16 @@ bool CBaseFormation::loadXML(CStr filename)
m_tag = CStr(Attr.Value); m_tag = CStr(Attr.Value);
else if ( Attr.Name == at_bonus ) else if ( Attr.Name == at_bonus )
m_bonus = CStr(Attr.Value); m_bonus = CStr(Attr.Value);
else if ( Attr.Name == at_bonusbase )
m_bonusBase = CStr(Attr.Value);
else if ( Attr.Name == at_bonustype ) else if ( Attr.Name == at_bonustype )
m_bonusType = CStr(Attr.Value); m_bonusType = CStr(Attr.Value);
else if ( Attr.Name == at_bonusval ) else if ( Attr.Name == at_bonusval )
m_bonusVal = CStr(Attr.Value).ToFloat(); m_bonusVal = CStr(Attr.Value).ToFloat();
else if ( Attr.Name == at_penalty ) else if ( Attr.Name == at_penalty )
m_penalty = CStr(Attr.Value); m_penalty = CStr(Attr.Value);
else if ( Attr.Name == at_penaltybase )
m_penaltyBase = CStr(Attr.Value);
else if ( Attr.Name == at_penaltytype ) else if ( Attr.Name == at_penaltytype )
m_penaltyType = CStr(Attr.Value); m_penaltyType = CStr(Attr.Value);
else if ( Attr.Name == at_penaltyval ) else if ( Attr.Name == at_penaltyval )
@ -200,4 +206,4 @@ void CBaseFormation::AssignCategory(int order, CStr category)
size_t off = category.find(temp); size_t off = category.find(temp);
category.erase( off, temp.length() ); category.erase( off, temp.length() );
} }
} }

View File

@ -29,10 +29,12 @@ public:
~CBaseFormation(){} ~CBaseFormation(){}
CStr GetBonus(){ return m_bonus; } CStr GetBonus(){ return m_bonus; }
CStr GetBonusBase(){ return m_bonusBase; }
CStr GetBonusType(){ return m_bonusType; } CStr GetBonusType(){ return m_bonusType; }
float GetBonusVal(){ return m_bonusVal; } float GetBonusVal(){ return m_bonusVal; }
CStr GetPenalty(){ return m_penalty; } CStr GetPenalty(){ return m_penalty; }
CStr GetPenaltyBase(){ return m_penaltyBase; }
CStr GetPenaltyType(){ return m_penaltyType; } CStr GetPenaltyType(){ return m_penaltyType; }
float GetPenaltyVal(){ return m_penaltyVal; } float GetPenaltyVal(){ return m_penaltyVal; }
@ -46,10 +48,12 @@ private:
CStr m_tag; CStr m_tag;
CStr m_bonus; CStr m_bonus;
CStr m_bonusBase;
CStr m_bonusType; CStr m_bonusType;
float m_bonusVal; float m_bonusVal;
CStr m_penalty; CStr m_penalty;
CStr m_penaltyBase;
CStr m_penaltyType; CStr m_penaltyType;
float m_penaltyVal; float m_penaltyVal;
@ -71,9 +75,9 @@ private:
//The key is the "order" of the slot //The key is the "order" of the slot
std::map<int, FormationSlot> m_slots; std::map<int, FormationSlot> m_slots;
std::vector<float> m_angleValues; //cosine of angle divisions
bool loadXML(CStr filename); bool loadXML(CStr filename);
void AssignCategory(int order, CStr category); //takes care of formatting strings void AssignCategory(int order, CStr category); //takes care of formatting strings
}; };
#endif #endif

View File

@ -36,21 +36,20 @@ using namespace std;
CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, const std::set<CStrW>& actorSelections, CStrW building ) CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, const std::set<CStrW>& actorSelections, CStrW building )
{ {
m_position = position; m_position = position;
m_orientation = orientation; m_orientation.Y = orientation;
m_ahead.x = sin( m_orientation ); m_ahead.x = sin( m_orientation.Y );
m_ahead.y = cos( m_orientation ); m_ahead.y = cos( m_orientation.Y );
// set sane default in case someone forgets to add this to the entity's // set sane default in case someone forgets to add this to the entity's
// XML file, which is prone to happen. (prevents crash below when // XML file, which is prone to happen. (prevents crash below when
// using this value to resize a vector) // using this value to resize a vector)
const int sane_sectordivs_default = 4; const int sane_sectordivs_default = 4;
m_sectorDivs = sane_sectordivs_default;
/* Anything added to this list MUST be added to BaseEntity.cpp (and variables used should /* Anything added to this list MUST be added to BaseEntity.cpp (and variables used should
also be added to BaseEntity.h */ also be added to BaseEntity.h */
AddProperty( L"actions.move.speed", &m_speed ); AddProperty( L"actions.move.speed_curr", &m_speed );
AddProperty( L"actions.move.run.speed", &( m_run.m_Speed ) ); AddProperty( L"actions.move.run.speed.curr", &( m_run.m_Speed ) );
AddProperty( L"actions.move.run.rangemin", &( m_run.m_MinRange ) ); AddProperty( L"actions.move.run.rangemin", &( m_run.m_MinRange ) );
AddProperty( L"actions.move.run.range", &( m_run.m_MaxRange ) ); AddProperty( L"actions.move.run.range", &( m_run.m_MaxRange ) );
AddProperty( L"actions.move.run.regen_rate", &m_runRegenRate ); AddProperty( L"actions.move.run.regen_rate", &m_runRegenRate );
@ -61,7 +60,7 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons
AddProperty( L"traits.corpse", &m_corpse ); AddProperty( L"traits.corpse", &m_corpse );
AddProperty( L"actions.move.turningradius", &m_turningRadius ); AddProperty( L"actions.move.turningradius", &m_turningRadius );
AddProperty( L"position", &m_graphics_position, false, (NotifyFn)&CEntity::teleport ); AddProperty( L"position", &m_graphics_position, false, (NotifyFn)&CEntity::teleport );
AddProperty( L"orientation", &m_graphics_orientation, false, (NotifyFn)&CEntity::reorient ); AddProperty( L"orientation", &(m_orientation.Y), false, (NotifyFn)&CEntity::reorient );
AddProperty( L"player", &m_player, false, (NotifyFn)&CEntity::playerChanged ); AddProperty( L"player", &m_player, false, (NotifyFn)&CEntity::playerChanged );
AddProperty( L"traits.health.curr", &m_healthCurr ); AddProperty( L"traits.health.curr", &m_healthCurr );
AddProperty( L"traits.health.max", &m_healthMax ); AddProperty( L"traits.health.max", &m_healthMax );
@ -83,10 +82,8 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons
AddProperty( L"traits.stamina.border_height", &m_staminaBorderHeight); AddProperty( L"traits.stamina.border_height", &m_staminaBorderHeight);
AddProperty( L"traits.stamina.border_width", &m_staminaBorderWidth ); AddProperty( L"traits.stamina.border_width", &m_staminaBorderWidth );
AddProperty( L"traits.stamina.border_name", &m_staminaBorderName ); AddProperty( L"traits.stamina.border_name", &m_staminaBorderName );
AddProperty( L"traits.angle_penalty.sectors", &m_sectorDivs); AddProperty( L"traits.flank_penalty.sectors", &m_sectorDivs);
AddProperty( L"traits.angle_penalty.value", &m_sectorPenalty ); AddProperty( L"traits.pitch.sectors", &m_pitchDivs );
AddProperty( L"traits.pitch.max_actor", &m_maxActorPitch );
AddProperty( L"traits.pitch.min_actor", &m_minActorPitch );
AddProperty( L"traits.rank.width", &m_rankWidth ); AddProperty( L"traits.rank.width", &m_rankWidth );
AddProperty( L"traits.rank.height", &m_rankHeight ); AddProperty( L"traits.rank.height", &m_rankHeight );
AddProperty( L"traits.rank.name", &m_rankName ); AddProperty( L"traits.rank.name", &m_rankName );
@ -95,6 +92,9 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons
AddProperty( L"traits.minimap.green", &m_minimapG ); AddProperty( L"traits.minimap.green", &m_minimapG );
AddProperty( L"traits.minimap.blue", &m_minimapB ); AddProperty( L"traits.minimap.blue", &m_minimapB );
AddProperty( L"traits.anchor.type", &m_anchorType ); AddProperty( L"traits.anchor.type", &m_anchorType );
AddProperty( L"traits.anchor.conformx", &m_anchorConformX );
AddProperty( L"traits.anchor.conformz", &m_anchorConformZ );
AddProperty( L"traits.vision.los", &m_los ); AddProperty( L"traits.vision.los", &m_los );
AddProperty( L"traits.vision.permanent", &m_permanent ); AddProperty( L"traits.vision.permanent", &m_permanent );
AddProperty( L"last_combat_time", &m_lastCombatTime ); AddProperty( L"last_combat_time", &m_lastCombatTime );
@ -109,23 +109,6 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons
AddHandler( t, &m_EventHandlers[t] ); AddHandler( t, &m_EventHandlers[t] );
} }
// this has been the cause of several crashes (due to not being
// specified in the XML file), so in addition to the above default,
// we'll sanity check its value.
if(!(0 <= m_sectorDivs && m_sectorDivs < 1000))
{
debug_warn("invalid entity angle_penalty.sectors value");
m_sectorDivs = sane_sectordivs_default;
}
m_sectorAngles.resize(m_sectorDivs);
m_sectorValues.resize(m_sectorDivs);
float step = DEGTORAD(360.0f / m_sectorDivs);
for ( int i=0; i<m_sectorDivs; ++i )
{
m_sectorAngles[i] = cosf( step*i );
m_sectorValues[i] = false;
}
m_collisionPatch = NULL; m_collisionPatch = NULL;
// Set our parent unit and build us an actor. // Set our parent unit and build us an actor.
@ -141,6 +124,18 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons
m_actorSelections = actorSelections; m_actorSelections = actorSelections;
loadBase(); loadBase();
// this has been the cause of several crashes (due to not being
// specified in the XML file), so in addition to the above default,
// we'll sanity check its value.
if(!(0 <= m_sectorDivs && m_sectorDivs < 360))
{
debug_warn("invalid entity flank_penalty.sectors value");
m_sectorDivs = sane_sectordivs_default;
}
m_sectorValues.resize(m_sectorDivs);
for ( int i=0; i<m_sectorDivs; ++i )
m_sectorValues[i] = false;
if( m_bounds ) if( m_bounds )
m_bounds->setPosition( m_position.X, m_position.Z ); m_bounds->setPosition( m_position.X, m_position.Z );
@ -151,7 +146,6 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons
m_graphics_orientation = m_orientation; m_graphics_orientation = m_orientation;
m_actor_transform_valid = false; m_actor_transform_valid = false;
m_pitchOrientation = m_pitchOrientation_previous = m_graphics_pitchOrientation = 0.0f;
m_destroyed = false; m_destroyed = false;
m_selected = false; m_selected = false;
@ -172,7 +166,7 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons
m_grouped = -1; m_grouped = -1;
m_building = building; m_building = building;
m_player = g_Game->GetPlayer( 0 ); m_player = g_Game->GetPlayer( 0 );
Initialize(); Initialize();
@ -198,7 +192,8 @@ CEntity::~CEntity()
delete it->second; delete it->second;
} }
m_auras.clear(); m_auras.clear();
m_destroyNotifiers=true;
for ( size_t i=0; i<m_listeners.size(); i++ ) for ( size_t i=0; i<m_listeners.size(); i++ )
m_listeners[i].m_sender->DestroyNotifier( this ); m_listeners[i].m_sender->DestroyNotifier( this );
DestroyAllNotifiers(); DestroyAllNotifiers();
@ -249,7 +244,10 @@ void CEntity::kill()
CEntity* remove = this; CEntity* remove = this;
g_FormationManager.RemoveUnit(remove); g_FormationManager.RemoveUnit(remove);
m_destroyNotifiers=true;
for ( size_t i=0; i<m_listeners.size(); i++ )
m_listeners[i].m_sender->DestroyNotifier( this );
DestroyAllNotifiers(); DestroyAllNotifiers();
if( m_bounds ) if( m_bounds )
@ -286,22 +284,22 @@ void CEntity::SetPlayer(CPlayer *pPlayer)
void CEntity::updateActorTransforms() void CEntity::updateActorTransforms()
{ {
CMatrix3D m; CMatrix3D m;
CMatrix3D mX; CMatrix3D mXZ;
float Cos = cosf( m_graphics_orientation ); float Cos = cosf( m_graphics_orientation.Y );
float Sin = sinf( m_graphics_orientation ); float Sin = sinf( m_graphics_orientation.Y );
m._11=-Cos; m._12=0.0f; m._13=-Sin; m._14=0.0f; m._11=-Cos; m._12=0.0f; m._13=-Sin; m._14=0.0f;
m._21=0.0f; m._22=1.0f; m._23=0.0f; m._24=0.0f; m._21=0.0f; m._22=1.0f; m._23=0.0f; m._24=0.0f;
m._31=Sin; m._32=0.0f; m._33=-Cos; m._34=0.0f; m._31=Sin; m._32=0.0f; m._33=-Cos; m._34=0.0f;
m._41=0.0f; m._42=0.0f; m._43=0.0f; m._44=1.0f; m._41=0.0f; m._42=0.0f; m._43=0.0f; m._44=1.0f;
mX.SetXRotation( m_graphics_pitchOrientation ); mXZ.SetXRotation( m_graphics_orientation.X );
mX = m*mX; mXZ.RotateZ( m_graphics_orientation.Z );
mX.Translate(m_graphics_position); mXZ = m*mXZ;
//m.RotateX(m_graphics_pitchOrientation); mXZ.Translate(m_graphics_position);
if( m_actor ) if( m_actor )
m_actor->GetModel()->SetTransform( mX ); m_actor->GetModel()->SetTransform( mXZ );
} }
void CEntity::snapToGround() void CEntity::snapToGround()
@ -384,7 +382,6 @@ void CEntity::update( size_t timestep )
{ {
m_position_previous = m_position; m_position_previous = m_position;
m_orientation_previous = m_orientation; m_orientation_previous = m_orientation;
m_pitchOrientation_previous = m_pitchOrientation;
CalculateRun( timestep ); CalculateRun( timestep );
CalculateHealth( timestep ); CalculateHealth( timestep );
@ -756,7 +753,7 @@ struct isListenerSender
int CEntity::DestroyNotifier( CEntity* target ) int CEntity::DestroyNotifier( CEntity* target )
{ {
if (target->m_listeners.empty() || !m_destroyNotifiers) if ( target->m_listeners.empty() )
return 0; return 0;
//Stop listening //Stop listening
// (Don't just loop and use 'erase', because modifying the deque while // (Don't just loop and use 'erase', because modifying the deque while
@ -812,9 +809,9 @@ void CEntity::repath()
void CEntity::reorient() void CEntity::reorient()
{ {
m_orientation = m_graphics_orientation; m_orientation = m_graphics_orientation;
m_pitchOrientation = m_graphics_pitchOrientation;
m_ahead.x = sin( m_orientation ); m_ahead.x = sin( m_orientation.Y );
m_ahead.y = cos( m_orientation ); m_ahead.y = cos( m_orientation.Y );
if( m_bounds->m_type == CBoundingObject::BOUND_OABB ) if( m_bounds->m_type == CBoundingObject::BOUND_OABB )
((CBoundingBox*)m_bounds)->setOrientation( m_ahead ); ((CBoundingBox*)m_bounds)->setOrientation( m_ahead );
updateActorTransforms(); updateActorTransforms();
@ -858,24 +855,27 @@ void CEntity::checkGroup()
void CEntity::interpolate( float relativeoffset ) void CEntity::interpolate( float relativeoffset )
{ {
CVector3D old_graphics_position = m_graphics_position; CVector3D old_graphics_position = m_graphics_position;
float old_graphics_orientation = m_graphics_orientation; CVector3D old_graphics_orientation = m_graphics_orientation;
float old_graphics_pitchOrientation = m_graphics_pitchOrientation;
m_graphics_position = Interpolate<CVector3D>( m_position_previous, m_position, relativeoffset ); m_graphics_position = Interpolate<CVector3D>( m_position_previous, m_position, relativeoffset );
// Avoid wraparound glitches for interpolating angles. // Avoid wraparound glitches for interpolating angles.
while( m_orientation < m_orientation_previous - PI ) while( m_orientation.Y < m_orientation_previous.Y - PI )
m_orientation_previous -= 2 * PI; m_orientation_previous.Y -= 2 * PI;
while( m_orientation > m_orientation_previous + PI ) while( m_orientation.Y > m_orientation_previous.Y + PI )
m_orientation_previous += 2 * PI; m_orientation_previous.Y += 2 * PI;
while( m_pitchOrientation < m_pitchOrientation_previous - PI ) while( m_orientation.X < m_orientation_previous.X - PI )
m_pitchOrientation_previous -= 2 * PI; m_orientation_previous.X -= 2 * PI;
while( m_pitchOrientation > m_pitchOrientation_previous + PI ) while( m_orientation.X > m_orientation_previous.X + PI )
m_pitchOrientation_previous += 2 * PI; m_orientation_previous.X += 2 * PI;
m_graphics_orientation = Interpolate<float>( m_orientation_previous, m_orientation, relativeoffset ); while( m_orientation.Z < m_orientation_previous.Z - PI )
m_graphics_pitchOrientation = Interpolate<float>( m_pitchOrientation_previous, m_pitchOrientation, relativeoffset ); m_orientation_previous.Z -= 2 * PI;
while( m_orientation.Z > m_orientation_previous.Z + PI )
m_orientation_previous.Z += 2 * PI;
m_graphics_orientation = Interpolate<CVector3D>( m_orientation_previous, m_orientation, relativeoffset );
// Mark the actor transform data as invalid if the entity has moved since // Mark the actor transform data as invalid if the entity has moved since
// the last call to 'interpolate'. // the last call to 'interpolate'.
@ -884,8 +884,7 @@ void CEntity::interpolate( float relativeoffset )
// handle flying units or moving terrain. // handle flying units or moving terrain.
if( m_graphics_orientation != old_graphics_orientation || if( m_graphics_orientation != old_graphics_orientation ||
m_graphics_position.X != old_graphics_position.X || m_graphics_position.X != old_graphics_position.X ||
m_graphics_position.Z != old_graphics_position.Z || m_graphics_position.Z != old_graphics_position.Z
m_graphics_pitchOrientation != old_graphics_pitchOrientation
) )
{ {
m_actor_transform_valid = false; m_actor_transform_valid = false;
@ -999,7 +998,39 @@ float CEntity::getAnchorLevel( float x, float z )
return max( groundLevel, g_Renderer.GetWaterManager()->m_WaterHeight ); return max( groundLevel, g_Renderer.GetWaterManager()->m_WaterHeight );
} }
} }
int CEntity::findSector( int divs, float angle, float maxAngle, bool negative )
{
float step=maxAngle/divs;
if ( negative )
{
float tracker;
int i=1, sectorRemainder;
for ( tracker=-maxAngle/2.0f; tracker+step<0.0f; tracker+=step, ++i )
{
if ( angle > tracker && angle <= tracker+step )
return i;
}
sectorRemainder = i;
i=divs;
for ( tracker=maxAngle/2.0f; tracker-step>0.0f; tracker-=step, --i )
{
if ( angle < tracker && angle >= tracker-step )
return i;
}
return sectorRemainder;
}
else
{
int i=0;
for ( float tracker=0.0f; tracker<maxAngle; tracker+=step, ++i )
{
if ( angle > tracker && angle <= tracker+step )
return i;
}
}
debug_warn("CEntity::findSector() - invalid parameters passed.");
return -1;
}
void CEntity::renderSelectionOutline( float alpha ) void CEntity::renderSelectionOutline( float alpha )
{ {
if( !m_bounds ) if( !m_bounds )
@ -1047,8 +1078,8 @@ void CEntity::renderSelectionOutline( float alpha )
float d = ((CBoundingBox*)m_bounds)->m_d; float d = ((CBoundingBox*)m_bounds)->m_d;
float w = ((CBoundingBox*)m_bounds)->m_w; float w = ((CBoundingBox*)m_bounds)->m_w;
u.x = sin( m_graphics_orientation ); u.x = sin( m_graphics_orientation.Y );
u.y = cos( m_graphics_orientation ); u.y = cos( m_graphics_orientation.Y );
v.x = u.y; v.x = u.y;
v.y = -u.x; v.y = -u.x;
@ -1316,6 +1347,7 @@ void CEntity::ScriptingInit()
AddMethod<jsval, &CEntity::AddAura>( "addAura", 3 ); AddMethod<jsval, &CEntity::AddAura>( "addAura", 3 );
AddMethod<jsval, &CEntity::RemoveAura>( "removeAura", 1 ); AddMethod<jsval, &CEntity::RemoveAura>( "removeAura", 1 );
AddMethod<jsval, &CEntity::SetActionParams>( "setActionParams", 5 ); AddMethod<jsval, &CEntity::SetActionParams>( "setActionParams", 5 );
AddMethod<int, &CEntity::GetCurrentRequest>( "getCurrentRequest", 0 );
AddMethod<bool, &CEntity::ForceCheckListeners>( "forceCheckListeners", 2 ); AddMethod<bool, &CEntity::ForceCheckListeners>( "forceCheckListeners", 2 );
AddMethod<bool, &CEntity::RequestNotification>( "requestNotification", 4 ); AddMethod<bool, &CEntity::RequestNotification>( "requestNotification", 4 );
AddMethod<jsval, &CEntity::DestroyNotifier>( "destroyNotifier", 1 ); AddMethod<jsval, &CEntity::DestroyNotifier>( "destroyNotifier", 1 );
@ -1331,7 +1363,9 @@ void CEntity::ScriptingInit()
AddMethod<jsval, &CEntity::GetFormationPenaltyType>( "getFormationPenaltyType", 0 ); AddMethod<jsval, &CEntity::GetFormationPenaltyType>( "getFormationPenaltyType", 0 );
AddMethod<jsval, &CEntity::GetFormationPenaltyVal>( "getFormationPenaltyVal", 0 ); AddMethod<jsval, &CEntity::GetFormationPenaltyVal>( "getFormationPenaltyVal", 0 );
AddMethod<jsval, &CEntity::RegisterDamage>( "registerDamage", 0 ); AddMethod<jsval, &CEntity::RegisterDamage>( "registerDamage", 0 );
AddMethod<jsval, &CEntity::RegisterIdle>( "registerIdle", 0 ); AddMethod<jsval, &CEntity::RegisterOrderChange>( "registerOrderChange", 0 );
AddMethod<jsval, &CEntity::GetAttackDirections>( "getAttackDirections", 0 );
AddMethod<jsval, &CEntity::FindSector>("findSector", 4);
AddClassProperty( L"template", (CBaseEntity* CEntity::*)&CEntity::m_base, false, (NotifyFn)&CEntity::loadBase ); AddClassProperty( L"template", (CBaseEntity* CEntity::*)&CEntity::m_base, false, (NotifyFn)&CEntity::loadBase );
AddClassProperty( L"traits.id.classes", (GetFn)&CEntity::getClassSet, (SetFn)&CEntity::setClassSet ); AddClassProperty( L"traits.id.classes", (GetFn)&CEntity::getClassSet, (SetFn)&CEntity::setClassSet );
@ -1793,8 +1827,7 @@ bool CEntity::RequestNotification( JSContext* cx, uintN argc, jsval* argv )
CEntity *target = ToNative<CEntity>( argv[0] ); CEntity *target = ToNative<CEntity>( argv[0] );
(int&)notify.m_type = ToPrimitive<int>( argv[1] ); (int&)notify.m_type = ToPrimitive<int>( argv[1] );
bool tmpDestroyNotifiers = ToPrimitive<bool>( argv[2] ); bool tmpDestroyNotifiers = ToPrimitive<bool>( argv[2] );
// TODO: ??? This local variable overrides the member variable of the same name... m_destroyNotifiers = !ToPrimitive<bool>( argv[3] );
bool m_destroyNotifiers = !ToPrimitive<bool>( argv[3] );
if (target == this) if (target == this)
return false; return false;
@ -1840,6 +1873,10 @@ bool CEntity::RequestNotification( JSContext* cx, uintN argc, jsval* argv )
target->m_listeners.push_back( notify ); target->m_listeners.push_back( notify );
return false; return false;
} }
int CEntity::GetCurrentRequest( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
return m_currentRequest;
}
bool CEntity::ForceCheckListeners( JSContext *cx, uintN argc, jsval* argv ) bool CEntity::ForceCheckListeners( JSContext *cx, uintN argc, jsval* argv )
{ {
if( argc < 2 ) if( argc < 2 )
@ -1952,6 +1989,10 @@ jsval CEntity::GetFormationPenalty( JSContext* UNUSED(cx), uintN UNUSED(argc), j
{ {
return ToJSVal( GetFormation()->GetBase()->GetPenalty() ); return ToJSVal( GetFormation()->GetBase()->GetPenalty() );
} }
jsval CEntity::GetFormationPenaltyBase( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
return ToJSVal( GetFormation()->GetBase()->GetPenaltyBase() );
}
jsval CEntity::GetFormationPenaltyType( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) jsval CEntity::GetFormationPenaltyType( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{ {
return ToJSVal( GetFormation()->GetBase()->GetPenaltyType() ); return ToJSVal( GetFormation()->GetBase()->GetPenaltyType() );
@ -1964,6 +2005,10 @@ jsval CEntity::GetFormationBonus( JSContext* UNUSED(cx), uintN UNUSED(argc), jsv
{ {
return ToJSVal( GetFormation()->GetBase()->GetBonus() ); return ToJSVal( GetFormation()->GetBase()->GetBonus() );
} }
jsval CEntity::GetFormationBonusBase( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
return ToJSVal( GetFormation()->GetBase()->GetBonusBase() );
}
jsval CEntity::GetFormationBonusType( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) jsval CEntity::GetFormationBonusType( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{ {
return ToJSVal( GetFormation()->GetBase()->GetBonusType() ); return ToJSVal( GetFormation()->GetBase()->GetBonusType() );
@ -1985,19 +2030,13 @@ jsval CEntity::RegisterDamage( JSContext* cx, uintN argc, jsval* argv )
CVector2D pos = CVector2D( inflictor->m_position.X, inflictor->m_position.Z ); CVector2D pos = CVector2D( inflictor->m_position.X, inflictor->m_position.Z );
CVector2D posDelta = (pos - m_position).normalize(); CVector2D posDelta = (pos - m_position).normalize();
float angle = up.dot(posDelta); float angle = acosf( up.dot(posDelta) );
//Find what section it is between and "activate" it //Find what section it is between and "activate" it
for ( int i=0; i<m_sectorDivs; ++i ) int sector = findSector(m_sectorDivs, angle, DEGTORAD(360.0f))-1;
{ m_sectorValues[sector]=true;
//Wrap around to the start-if we've made it this far, it's here
if ( i == m_base->m_sectorDivs )
m_sectorValues[i] = true;
else if ( angle > m_sectorAngles[i] && angle < m_sectorAngles[i+1] )
m_sectorValues[i] = true;
}
return JS_TRUE; return JS_TRUE;
} }
jsval CEntity::RegisterIdle( JSContext* cx, uintN argc, jsval* argv ) jsval CEntity::RegisterOrderChange( JSContext* cx, uintN argc, jsval* argv )
{ {
if ( argc < 1 ) if ( argc < 1 )
{ {
@ -2010,16 +2049,10 @@ jsval CEntity::RegisterIdle( JSContext* cx, uintN argc, jsval* argv )
CVector2D pos = CVector2D( idleEntity->m_position.X, idleEntity->m_position.Z ); CVector2D pos = CVector2D( idleEntity->m_position.X, idleEntity->m_position.Z );
CVector2D posDelta = (pos - m_position).normalize(); CVector2D posDelta = (pos - m_position).normalize();
float angle = up.dot(posDelta); float angle = acosf( up.dot(posDelta) );
//Find what section it is between and "activate" it //Find what section it is between and "deactivate" it
for ( int i=0; i<m_sectorDivs; ++i ) int sector = MAX( 0.0, findSector(m_sectorDivs, angle, DEGTORAD(360.0f)) );
{ m_sectorValues[sector]=false;
//Wrap around to the start-if we've made it this far, it's here
if ( i == m_base->m_sectorDivs )
m_sectorValues[i] = false;
else if ( angle > m_sectorAngles[i] && angle < m_sectorAngles[i+1] )
m_sectorValues[i] = false;
}
return JS_TRUE; return JS_TRUE;
} }
jsval CEntity::GetAttackDirections( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) jsval CEntity::GetAttackDirections( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
@ -2033,3 +2066,46 @@ jsval CEntity::GetAttackDirections( JSContext* UNUSED(cx), uintN UNUSED(argc), j
} }
return ToJSVal( directions ); return ToJSVal( directions );
} }
jsval CEntity::FindSector( JSContext* cx, uintN argc, jsval* argv )
{
if ( argc < 4 )
{
JS_ReportError( cx, "Too few parameters" );
return( false );
}
int divs = ToPrimitive<int>( argv[0] );
float angle = ToPrimitive<float>( argv[1] );
float maxAngle = ToPrimitive<float>( argv[2] );
bool negative = ToPrimitive<bool>( argv[3] );
float step = maxAngle/divs;
if ( negative )
{
float tracker;
int i=1, sectorRemainder;
for ( tracker=-maxAngle/2.0f; tracker+step<0.0f; tracker+=step, ++i )
{
if ( angle > tracker && angle <= tracker+step )
return ToJSVal(i);
}
sectorRemainder = i;
i=divs;
for ( tracker=maxAngle/2.0f; tracker-step>0.0f; tracker-=step, --i )
{
if ( angle < tracker && angle >= tracker-step )
return ToJSVal(i);
}
return ToJSVal(sectorRemainder);
}
else
{
int i=1;
for ( float tracker=0.0f; tracker<maxAngle; tracker+=step, ++i )
{
if ( angle > tracker && angle <= tracker+step )
return ToJSVal(i);
}
}
debug_warn("JS - FindSector(): invalid parameters");
return ToJSVal(-1);
}

View File

@ -149,6 +149,8 @@ public:
// Y anchor // Y anchor
CStrW m_anchorType; CStrW m_anchorType;
float m_anchorConformX;
float m_anchorConformZ;
// LOS // LOS
int m_los; int m_los;
@ -168,13 +170,12 @@ public:
CVector2D m_ahead; CVector2D m_ahead;
//-- Interpolated property //-- Interpolated property
float m_orientation; CVector3D m_orientation;
float m_orientation_previous; CVector3D m_orientation_previous;
float m_graphics_orientation; CVector3D m_graphics_orientation;
float m_pitchOrientation; CVector2D m_orientation_unclamped;
float m_pitchOrientation_previous;
float m_graphics_pitchOrientation;
// If the actor's current transform data is valid (i.e. the entity hasn't // If the actor's current transform data is valid (i.e. the entity hasn't
// moved since it was last calculated, and the terrain hasn't been changed). // moved since it was last calculated, and the terrain hasn't been changed).
@ -213,9 +214,9 @@ public:
bool m_destroyNotifiers; //True: we destroy them. False: the script does. bool m_destroyNotifiers; //True: we destroy them. False: the script does.
std::vector<bool> m_sectorValues; std::vector<bool> m_sectorValues;
std::vector<float> m_sectorAngles;
int m_sectorDivs; int m_sectorDivs;
float m_sectorPenalty;
int m_pitchDivs;
private: private:
CEntity( CBaseEntity* base, CVector3D position, float orientation, const std::set<CStrW>& actorSelections, CStrW building = L"" ); CEntity( CBaseEntity* base, CVector3D position, float orientation, const std::set<CStrW>& actorSelections, CStrW building = L"" );
@ -317,21 +318,26 @@ public:
int DestroyNotifier( CEntity* target ); //Stop notifier from sending to us int DestroyNotifier( CEntity* target ); //Stop notifier from sending to us
void DestroyAllNotifiers(); void DestroyAllNotifiers();
int findSector( int divs, float angle, float maxAngle, bool negative=true );
CEntityFormation* GetFormation(); CEntityFormation* GetFormation();
bool IsInClass( JSContext* cx, uintN argc, jsval* argv );
jsval GetFormationPenalty( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationPenalty( JSContext* cx, uintN argc, jsval* argv );
jsval GetFormationPenaltyBase( JSContext* cx, uintN argc, jsval* argv );
jsval GetFormationPenaltyType( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationPenaltyType( JSContext* cx, uintN argc, jsval* argv );
jsval GetFormationPenaltyVal( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationPenaltyVal( JSContext* cx, uintN argc, jsval* argv );
jsval GetFormationBonus( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationBonus( JSContext* cx, uintN argc, jsval* argv );
jsval GetFormationBonusBase( JSContext* cx, uintN argc, jsval* argv );
jsval GetFormationBonusType( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationBonusType( JSContext* cx, uintN argc, jsval* argv );
jsval GetFormationBonusVal( JSContext* cx, uintN argc, jsval* argv ); jsval GetFormationBonusVal( JSContext* cx, uintN argc, jsval* argv );
void DispatchFormationEvent( int type ); void DispatchFormationEvent( int type );
jsval RegisterDamage( JSContext* cx, uintN argc, jsval* argv ); jsval RegisterDamage( JSContext* cx, uintN argc, jsval* argv );
jsval RegisterIdle( JSContext* cx, uintN argc, jsval* argv ); jsval RegisterOrderChange( JSContext* cx, uintN argc, jsval* argv );
jsval GetAttackDirections( JSContext* cx, uintN argc, jsval* argv ); jsval GetAttackDirections( JSContext* cx, uintN argc, jsval* argv );
jsval FindSector( JSContext* cx, uintN argc, jsval* argv );
// Script constructor // Script constructor
static JSBool Construct( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval ); static JSBool Construct( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval );
@ -355,6 +361,7 @@ public:
bool RequestNotification( JSContext* cx, uintN argc, jsval* argv ); bool RequestNotification( JSContext* cx, uintN argc, jsval* argv );
//Just in case we want to explicitly check the listeners without waiting for the order to be pushed //Just in case we want to explicitly check the listeners without waiting for the order to be pushed
bool ForceCheckListeners( JSContext* cx, uintN argc, jsval* argv ); bool ForceCheckListeners( JSContext* cx, uintN argc, jsval* argv );
int GetCurrentRequest( JSContext* cx, uintN argc, jsval* argv );
void CheckListeners( int type, CEntity *target ); void CheckListeners( int type, CEntity *target );
jsval DestroyAllNotifiers( JSContext* cx, uintN argc, jsval* argv ); jsval DestroyAllNotifiers( JSContext* cx, uintN argc, jsval* argv );
jsval DestroyNotifier( JSContext* cx, uintN argc, jsval* argv ); jsval DestroyNotifier( JSContext* cx, uintN argc, jsval* argv );

View File

@ -7,7 +7,7 @@
#include "Profile.h" #include "Profile.h"
#include "Terrain.h" #include "Terrain.h"
#include "Game.h" #include "Game.h"
#include "MathUtil.h"
int SELECTION_CIRCLE_POINTS; int SELECTION_CIRCLE_POINTS;
int SELECTION_BOX_POINTS; int SELECTION_BOX_POINTS;
int SELECTION_SMOOTHNESS_UNIFIED = 9; int SELECTION_SMOOTHNESS_UNIFIED = 9;
@ -284,6 +284,30 @@ void CEntityManager::renderAll()
if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed ) if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed )
m_entities[i].m_entity->render(); m_entities[i].m_entity->render();
} }
void CEntityManager::conformAll()
{
PROFILE_START("conform all");
for ( int i=0; i < MAX_HANDLES; i++ )
{
if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed )
{
CEntity* entity = m_entities[i].m_entity;
CVector2D targetXZ = g_Game->GetWorld()->GetTerrain()->getSlopeAngleFace( entity->m_position.X, entity->m_position.Z, entity );
while( targetXZ.x > PI ) targetXZ.x -= 2 * PI;
while( targetXZ.x < -PI ) targetXZ.x += 2 * PI;
while( targetXZ.y > PI ) targetXZ.y -= 2 * PI;
while( targetXZ.y < -PI ) targetXZ.y += 2 * PI;
entity->m_orientation.X = clamp( targetXZ.x, -entity->m_anchorConformX, entity->m_anchorConformX );
entity->m_orientation.Z = clamp( targetXZ.y, -entity->m_anchorConformZ, entity->m_anchorConformZ );
entity->m_orientation_unclamped.x = targetXZ.x;
entity->m_orientation_unclamped.y = targetXZ.y;
entity->updateActorTransforms();
}
}
PROFILE_END("conform all");
}
void CEntityManager::invalidateAll() void CEntityManager::invalidateAll()
{ {

View File

@ -63,6 +63,7 @@ public:
void InitializeAll(); void InitializeAll();
void TickAll(); void TickAll();
void renderAll(); void renderAll();
void conformAll();
void invalidateAll(); void invalidateAll();
void deleteAll(); void deleteAll();

View File

@ -68,7 +68,8 @@ public:
NOTIFY_HEAL = 0x10, NOTIFY_HEAL = 0x10,
NOTIFY_GATHER = 0x20, NOTIFY_GATHER = 0x20,
NOTIFY_IDLE = 0x40, NOTIFY_IDLE = 0x40,
NOTIFY_ORDER_CHANGE = 0x80, //this isn't counted in NOTIFY_ALL
NOTIFY_ALL = 0x7F NOTIFY_ALL = 0x7F
} m_type; } m_type;

View File

@ -95,7 +95,7 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis,
// trig every time.right // trig every time.right
m_targetorientation = atan2( delta.x, delta.y ); m_targetorientation = atan2( delta.x, delta.y );
float deltatheta = m_targetorientation - (float)m_orientation; float deltatheta = m_targetorientation - (float)m_orientation.Y;
while( deltatheta > PI ) deltatheta -= 2 * PI; while( deltatheta > PI ) deltatheta -= 2 * PI;
while( deltatheta < -PI ) deltatheta += 2 * PI; while( deltatheta < -PI ) deltatheta += 2 * PI;
@ -106,10 +106,10 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis,
float maxTurningSpeed = ( m_speed / m_turningRadius ) * timestep; float maxTurningSpeed = ( m_speed / m_turningRadius ) * timestep;
deltatheta = clamp( deltatheta, -maxTurningSpeed, maxTurningSpeed ); deltatheta = clamp( deltatheta, -maxTurningSpeed, maxTurningSpeed );
} }
m_orientation = m_orientation + deltatheta; m_orientation.Y = m_orientation.Y + deltatheta;
m_ahead.x = sin( m_orientation ); m_ahead.x = sin( m_orientation.Y );
m_ahead.y = cos( m_orientation ); m_ahead.y = cos( m_orientation.Y );
// We can only really attempt to smooth paths the pathfinder // We can only really attempt to smooth paths the pathfinder
// has flagged for us. If the turning-radius calculations are // has flagged for us. If the turning-radius calculations are
@ -127,20 +127,29 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis,
// let's not. // let's not.
if( current->m_type != CEntityOrder::ORDER_GOTO_SMOOTHED ) if( current->m_type != CEntityOrder::ORDER_GOTO_SMOOTHED )
m_orientation = m_targetorientation; m_orientation.Y = m_targetorientation;
} }
else else
{ {
m_ahead = delta / len; m_ahead = delta / len;
m_orientation = m_targetorientation; m_orientation.Y = m_targetorientation;
} }
CEntity* _this = this;
CVector2D targetXZ = g_Game->GetWorld()->GetTerrain()->getSlopeAngleFace( m_position.X, m_position.Z, _this );
float targetpitch = g_Game->GetWorld()->GetTerrain()->getSlopeAngleFace( m_position.X, m_position.Z, m_orientation ); while( targetXZ.x > PI ) targetXZ.x -= 2 * PI;
while( targetpitch > PI ) targetpitch -= 2 * PI; while( targetXZ.x < -PI ) targetXZ.x += 2 * PI;
while( targetpitch < -PI ) targetpitch += 2 * PI; while( targetXZ.y > PI ) targetXZ.y -= 2 * PI;
m_pitchOrientation = clamp( targetpitch, m_minActorPitch, m_maxActorPitch ); while( targetXZ.y < -PI ) targetXZ.y += 2 * PI;
m_orientation.X = clamp( targetXZ.x, -m_anchorConformX, m_anchorConformX );
m_orientation.Z = clamp( targetXZ.y, -m_anchorConformZ, m_anchorConformZ );
m_orientation_unclamped.x = targetXZ.x;
m_orientation_unclamped.y = targetXZ.y;
CMovementEvent evt( m_orientation_unclamped.x );
DispatchEvent(&evt);
if( m_bounds && m_bounds->m_type == CBoundingObject::BOUND_OABB ) if( m_bounds && m_bounds->m_type == CBoundingObject::BOUND_OABB )
((CBoundingBox*)m_bounds)->setOrientation( m_ahead ); ((CBoundingBox*)m_bounds)->setOrientation( m_ahead );
@ -496,7 +505,7 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
else else
{ {
// Close enough, but turn to face them. // Close enough, but turn to face them.
m_orientation = atan2( delta.x, delta.y ); m_orientation.Y = atan2( delta.x, delta.y );
m_ahead = delta.normalize(); m_ahead = delta.normalize();
m_isRunning = false; m_isRunning = false;
} }

View File

@ -114,4 +114,10 @@ CIdleEvent::CIdleEvent( CEntityOrder order, int notifyType ) : CScriptEvent( L"i
AddLocalProperty( L"orderType", &m_orderType ); AddLocalProperty( L"orderType", &m_orderType );
AddLocalProperty( L"target", &m_target ); AddLocalProperty( L"target", &m_target );
AddLocalProperty( L"location", &m_location ); AddLocalProperty( L"location", &m_location );
}
CMovementEvent::CMovementEvent( float slope ) : CScriptEvent( L"movementEvent", EVENT_MOVEMENT, false )
{
m_slope = slope;
AddLocalProperty( L"slope", &m_slope );
} }

View File

@ -132,5 +132,10 @@ class CIdleEvent : public CScriptEvent
public: public:
CIdleEvent( CEntityOrder order, int notifyType ); CIdleEvent( CEntityOrder order, int notifyType );
}; };
class CMovementEvent : public CScriptEvent
{
float m_slope;
public:
CMovementEvent( float slope );
};
#endif #endif

View File

@ -491,7 +491,7 @@ BEGIN_COMMAND(RotateObject)
if (unit->GetEntity()) if (unit->GetEntity())
{ {
m_AngleOld = unit->GetEntity()->m_orientation; m_AngleOld = unit->GetEntity()->m_orientation.Y;
if (msg->usetarget) if (msg->usetarget)
{ {
CVector3D& pos = unit->GetEntity()->m_position; CVector3D& pos = unit->GetEntity()->m_position;
@ -543,7 +543,7 @@ BEGIN_COMMAND(RotateObject)
if (unit->GetEntity()) if (unit->GetEntity())
{ {
unit->GetEntity()->m_orientation = angle; unit->GetEntity()->m_orientation.Y = angle;
} }
else else
{ {