1
0
forked from 0ad/0ad

Delete ancient obsolete simulation code.

This was SVN commit r10903.
This commit is contained in:
Ykkrosh 2012-01-12 12:56:15 +00:00
parent ce67dfd333
commit 72bd886f80
24 changed files with 0 additions and 6778 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,533 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// Entity.h
//
// Entity class.
//
// 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. 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
// class is still executing. If you need to kill an entity,
// use CEntity::Kill(). If Kill() releases the final handle,
// the entity is placed in the reaper queue and is deleted immediately
// prior to its next update cycle.
//
// CUnit* m_actor: is the visible representation of this entity.
//
// 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.
//
#ifndef INCLUDED_ENTITY
#define INCLUDED_ENTITY
#include <boost/unordered_map.hpp>
#include <deque>
#include "scripting/ScriptableComplex.h"
#include "ps/Vector2D.h"
#include "maths/Vector3D.h"
#include "EntityOrders.h"
#include "EntityHandles.h"
#include "ScriptObject.h"
#include "EntitySupport.h"
#include "scripting/DOMEvent.h"
#include "scripting/ScriptCustomTypes.h"
#include "ps/scripting/JSCollection.h"
class CAura;
class CEntityTemplate;
class CBoundingObject;
class CPlayer;
class CProductionQueue;
class CSkeletonAnim;
class CUnit;
class CTerritory;
class CEntityFormation;
class CStance;
//
enum EntityFlags
{
// If this unit has been removed from the gameworld but has still
// has references.
ENTF_DESTROYED = 0x0001,
// State transition in the FSM (animations should be reset)
ENTF_TRANSITION = 0x0002,
ENTF_HAS_RALLY_POINT = 0x0004,
ENTF_HEALTH_DECAY = 0x0008,
// if set, we destroy them; otherwise, the script does.
ENTF_DESTROY_NOTIFIERS = 0x0010,
// is it actually running
ENTF_IS_RUNNING = 0x0020,
// if run was issued, it will remain true until it is stopped
ENTF_SHOULD_RUN = 0x0040,
// used in SetRun, corrects 1 frame stamina imbalance
ENTF_TRIGGER_RUN = 0x0080
};
// TODO MT: Put this is /some/ sort of order...
class CEntity : public CJSComplex<CEntity>, public IEventTarget
{
NONCOPYABLE(CEntity);
friend class CEntityManager;
friend class CUnit;
typedef boost::unordered_map<CStrW, CAura*> AuraTable;
typedef boost::unordered_map<int, SEntityAction> ActionTable;
typedef std::set<CAura*> AuraSet;
public:
// Intrinsic properties
CEntityTemplate* m_base;
// The class types this entity has
CClassSet m_classes;
// Production queue
CProductionQueue* m_productionQueue;
// Unit AI stance
CStance* m_stance;
// Movement properties
float m_speed;
float m_turningRadius;
float m_runSpeed;
float m_runRegenRate;
float m_runDecayRate;
float m_maxActorPitch;
float m_minActorPitch;
float m_healthRegenRate;
float m_healthRegenStart;
float m_healthDecayRate;
float m_runMaxRange;
float m_runMinRange;
ActionTable m_actions;
bool m_selected;
ssize_t m_grouped; // group index or -1 if not grouped
ssize_t m_formation; // Index of which formation we're in or -1
ssize_t m_formationSlot; // The slot of the above formation
int ent_flags;
bool entf_get(int desired_flag) const { return (ent_flags & desired_flag) != 0; }
void entf_set(int desired_flag) { ent_flags |= desired_flag; }
void entf_clear(int desired_flag) { ent_flags &= ~desired_flag; }
void entf_set_to(int desired_flag, bool value)
{
ent_flags &= ~desired_flag;
const int mask = value? ~0u : 0;
ent_flags |= desired_flag & mask;
}
// If this unit is still active in the gameworld - i.e. not a corpse.
bool m_extant;
// If this is false, the unit will not be drawn and cannot be interacted with using the mouse.
bool m_visible;
int m_frameCheck; //counts the frame
double m_lastCombatTime;
double m_lastRunTime;
// Building to convert to if this is a foundation, or "" otherwise
CStrW m_building;
//SP properties
float m_staminaCurr;
float m_staminaMax;
// HP properties
float m_healthCurr;
float m_healthMax;
// Rank properties
CStr m_rankName;
// Stance name
CStr m_stanceName;
// LOS distance/range
ssize_t m_los;
// If the object is a territory centre, this points to its territory
CTerritory* m_associatedTerritory;
// Territorial limit
CStrW m_buildingLimitCategory;
// Auras
AuraTable m_auras;
AuraSet m_aurasInfluencingMe;
//-- Interpolated property
CVector3D m_position;
CVector3D m_position_previous;
CVector3D m_graphics_position;
CBoundingObject* m_bounds;
float m_targetorientation;
CVector2D m_ahead;
//-- Interpolated property
CVector3D m_orientation;
CVector3D m_orientation_smoothed; // used for slow graphical-only rotation - tends towards m_orientation
CVector3D m_orientation_previous; // previous smoothed value
CVector3D m_graphics_orientation;
CVector2D m_orientation_unclamped;
CVector2D m_rallyPoint; // valid iff ENT_HAS_RALLY_POINT
// 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).
bool m_actor_transform_valid;
// Our current collision patch in CEntityManager
std::vector<CEntity*>* m_collisionPatch;
//-- Scripts
// Get script execution contexts - always run in the context of the entity that fired it.
JSObject* GetScriptExecContext( IEventTarget* target ) { return( ((CEntity*)target)->GetScript() ); }
CScriptObject m_EventHandlers[EVENT_LAST];
CUnit* m_actor;
std::set<CStr8> m_actorSelections;
int m_lastState; // used in animation FSM
// Position in the current state's cycle
static const int NOT_IN_CYCLE = -1;
int m_fsm_cyclepos; // -cycle_length....cycle_length
CEntityOrders m_orderQueue;
std::deque<CEntityListener> m_listeners;
std::vector<CEntity*> m_notifiers;
int m_currentNotification; //Current order in the form of a notification code
int m_currentRequest; //Notification we our notifiers are sending
std::vector<bool> m_sectorValues;
//Slight optimization for aura rendering
std::vector< std::vector<CVector2D> > m_unsnappedPoints;
//Kai: add id to identify the entity in the polygon soup
// needed for update/remove entities in the triangulation.
int m_dcdtId;
void removeObstacle();
private:
CEntity( CEntityTemplate* base, CVector3D position, float orientation, const std::set<CStr8>& actorSelections, const CStrW* building = 0 );
// (returns EGotoSituation, but we don't want to expose that)
int ProcessGotoHelper( CEntityOrder* current, int timestep_millis, HEntity& collide, float& timeLeft );
bool ProcessContactAction( CEntityOrder* current, int timestep_millis, bool repath_if_needed );
bool ProcessContactActionNoPathing( CEntityOrder* current, int timestep_millis );
bool ProcessProduce( CEntityOrder* order );
bool ProcessGotoNoPathing( CEntityOrder* current, int timestep_millis );
bool ProcessGotoNoPathingContact( CEntityOrder* current, int timestep_millis );
bool ProcessGoto( CEntityOrder* current, int timestep_millis );
bool ProcessGotoWaypoint( CEntityOrder* current, int timestep_millis, bool contact );
bool ProcessPatrol( CEntityOrder* current, int timestep_millis );
float ChooseMovementSpeed( float distance );
bool ShouldRun( float distance ); // Given our distance to a target, can we be running?
void UpdateOrders( int timestep_millis );
public:
~CEntity();
// Handle-to-self.
HEntity me;
// Updates gameplay information for the specified timestep
void Update( int timestep_millis );
// Updates graphical information for a point between the last and current
// simulation frame; should be 0 <= relativeoffset <= 1 (else it'll be
// clamped)
void Interpolate( float relativeoffset );
// Forces update of actor information during next call to 'interpolate'.
// (Necessary when terrain might move underneath the actor.)
void InvalidateActor();
// Updates auras
void UpdateAuras( int timestep_millis );
// Exit auras we're currently in (useful for example when we change state)
void ExitAuras();
// Removes entity from the gameworld and deallocates it, but not necessarily immediately.
// The keepActor parameter specifies whether to remove the unit's actor immediately (for
// units we want removed immediately, e.g. in Alkas) or to keep it and play the death
// animation (for units that die of "natural causes").
void Kill(bool keepActor = false);
// Process initialization
bool Initialize();
void initAuraData();
// Process tick.
// void Tick(); // (see comment in CEntityManager::UpdateAll)
// Calculate distances along the terrain
float Distance2D( float x, float z )
{
float dx = x - m_position.X;
float dz = z - m_position.Z;
return sqrt( dx*dx + dz*dz );
}
float Distance2D( CEntity* other )
{
return Distance2D( other->m_position.X, other->m_position.Z );
}
float Distance2D( CVector2D p )
{
return Distance2D( p.x, p.y );
}
private:
// The player that owns this entity.
// (Player is set publicly via CUnit::SetPlayerID)
CPlayer* m_player;
void SetPlayer(CPlayer* player);
public:
// Retrieve the player associated with this entity.
CPlayer* GetPlayer() { return m_player; }
// Update collision patch (move ourselves to a new one if necessary)
void UpdateCollisionPatch();
float GetAnchorLevel( float x, float z );
void SnapToGround();
void UpdateActorTransforms();
void UpdateXZOrientation();
// Getter and setter for the class sets
jsval GetClassSet();
void SetClassSet( jsval value );
void RebuildClassSet();
// Things like selection circles and debug info - possibly move to gui if/when it becomes responsible for (and capable of) it.
void Render();
void RenderSelectionOutline( float alpha = 1.0f );
void RenderAuras();
void RenderBars();
void RenderBarBorders();
void RenderHealthBar();
void RenderStaminaBar();
void RenderRank();
void RenderRallyPoint();
// Utility functions for rendering:
// Draw rectangle around the given centre, aligned with the given axes
void DrawRect(CVector3D& centre, CVector3D& up, CVector3D& right, float x1, float y1, float x2, float y2);
void DrawBar(CVector3D& centre, CVector3D& up, CVector3D& right,
float x1, float y1, float x2, float y2,
SColour col1, SColour col2, float currVal, float maxVal);
CVector2D GetScreenCoords( float height );
// After a collision, recalc the path to the next fixed waypoint.
void Repath();
//Calculate stamina points
void CalculateRegen(float timestep);
// Reset properties after the entity-template we use changes.
void LoadBase();
static void InitAttributes(const CEntity* _this);
void Reorient(); // Orientation
void Teleport(); // Fixes things if the position is changed by something externally.
void StanceChanged(); // Sets m_stance to the right CStance object when our stance property is changed through scripts
void CheckSelection(); // In case anyone tries to select/deselect this through JavaScript.
void CheckGroup(); // Groups
void CheckExtant(); // Existence
void ClearOrders();
void PopOrder(); //Use this if an order has finished instead of m_orderQueue.pop_front()
void PushOrder( const CEntityOrder& order );
void DispatchNotification( const CEntityOrder& order, int type );
int DestroyNotifier( CEntity* target ); //Stop notifier from sending to us
void DestroyAllNotifiers();
int FindSector( int divs, float angle, float maxAngle, bool negative=true );
jsval_t FlattenTerrain( JSContext* cx, uintN argc, jsval* argv );
CEntityFormation* GetFormation();
jsval_t GetFormationPenalty( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetFormationPenaltyBase( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetFormationPenaltyType( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetFormationPenaltyVal( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetFormationBonus( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetFormationBonusBase( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetFormationBonusType( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetFormationBonusVal( JSContext* cx, uintN argc, jsval* argv );
void DispatchFormationEvent( int type );
jsval_t RegisterDamage( JSContext* cx, uintN argc, jsval* argv );
jsval_t RegisterOrderChange( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetAttackDirections( JSContext* cx, uintN argc, jsval* argv );
jsval_t FindSector( JSContext* cx, uintN argc, jsval* argv );
// Script constructor
static JSBool Construct( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval );
// Script-bound functions
jsval_t ToString( JSContext* cx, uintN argc, jsval* argv );
bool Kill( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetSpawnPoint( JSContext* cx, uintN argc, jsval* argv );
jsval_t HasRallyPoint( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetRallyPoint( JSContext* cx, uintN argc, jsval* argv );
jsval_t SetRallyPointAtCursor( JSContext* cx, uintN argc, jsval* argv );
jsval_t AddAura( JSContext* cx, uintN argc, jsval* argv );
jsval_t RemoveAura( JSContext* cx, uintN argc, jsval* argv );
jsval_t SetActionParams( JSContext* cx, uintN argc, jsval* argv );
jsval_t TriggerRun( JSContext* cx, uintN argc, jsval* argv );
jsval_t SetRun( JSContext* cx, uintN argc, jsval* argv );
jsval_t IsRunning( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetRunState( JSContext* cx, uintN argc, jsval* argv );
jsval_t OnDamaged( JSContext* cx, uintN argc, jsval* argv );
jsval_t GetVisibleEntities( JSContext* cx, uintN argc, jsval* argv );
float GetDistance( 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
bool ForceCheckListeners( JSContext* cx, uintN argc, jsval* argv );
int GetCurrentRequest( JSContext* cx, uintN argc, jsval* argv );
void CheckListeners( int type, CEntity *target );
jsval_t DestroyAllNotifiers( JSContext* cx, uintN argc, jsval* argv );
jsval_t DestroyNotifier( JSContext* cx, uintN argc, jsval* argv );
jsval JSI_GetPlayer();
void JSI_SetPlayer(jsval val);
bool IsInFormation( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
return ( m_formation != 0 ? true : false );
}
bool Order( JSContext* cx, uintN argc, jsval* argv, CEntityOrder::EOrderSource source, bool Queued );
// TODO: Replace these variants of Order() with a single function, and update scripts accordingly.
inline bool OrderSingle( JSContext* cx, uintN argc, jsval* argv )
{
return( Order( cx, argc, argv, CEntityOrder::SOURCE_PLAYER, false ) );
}
inline bool OrderQueued( JSContext* cx, uintN argc, jsval* argv )
{
return( Order( cx, argc, argv, CEntityOrder::SOURCE_PLAYER, true ) );
}
inline bool OrderFromTriggers( JSContext* cx, uintN argc, jsval* argv )
{
return( Order( cx, argc, argv, CEntityOrder::SOURCE_TRIGGERS, true ) );
}
bool IsIdle( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
return( m_orderQueue.empty() );
}
bool IsDestroyed( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
return( entf_get(ENTF_DESTROYED) );
}
bool HasClass( JSContext* cx, uintN argc, jsval* argv )
{
debug_assert( argc >= 1 );
return( m_classes.IsMember( ToPrimitive<CStrW>( cx, argv[0] ) ) );
}
jsval_t TerminateOrder( JSContext* UNUSED(cx), uintN argc, jsval* argv )
{
debug_assert( argc >= 1);
if ( ToPrimitive<bool>( argv[0] ) )
m_orderQueue.clear();
else
m_orderQueue.pop_front();
return JSVAL_VOID;
}
jsval_t GetHeight( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
return ToJSVal(m_position.Y);
}
static void ScriptingInit();
// Functions that call script code
int GetAttackAction( HEntity target );
};
// General entity globals
// In its current incarnation, inefficient but pretty
#define SELECTION_TERRAIN_CONFORMANCE
extern int SELECTION_CIRCLE_POINTS;
extern int SELECTION_BOX_POINTS;
extern int SELECTION_SMOOTHNESS_UNIFIED;
extern int AURA_CIRCLE_POINTS;
#endif

View File

@ -1,267 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "EntityFormation.h"
#include "Entity.h"
#include "FormationCollection.h"
#include "FormationManager.h"
#include "Simulation.h"
#include "ps/Game.h"
#include "network/NetMessage.h"
CEntityFormation::CEntityFormation( CFormation*& base, size_t index )
{
if (!base)
return;
m_self = m_base = base;
m_numEntities=0;
m_speed=0.0f;
m_orientation = 0.0f;
m_position.x = m_position.y = -1.0f;
m_duplication=false;
m_entities.resize(m_base->m_numSlots);
m_index = index;
}
CEntityFormation::~CEntityFormation()
{
for ( size_t i=0; i<m_base->m_numSlots; ++i )
{
if ( m_entities[i] )
{
m_entities[i]->m_formation = -1;
m_entities[i]->m_formationSlot = (size_t)-1;
m_entities[i] = NULL;
}
}
}
void CEntityFormation::SwitchBase( CFormation*& base )
{
std::vector<CEntity*> copy;
copy.resize( m_base->m_numSlots );
for ( size_t i=0; i < m_base->m_numSlots; ++i )
{
if ( !m_entities[i] )
continue;
copy.push_back( m_entities[i] );
RemoveUnit( m_entities[i] );
}
m_self = m_base = base;
m_entities.resize(m_base->m_numSlots);
ResetAllEntities();
for ( std::vector<CEntity*>::iterator it=copy.begin(); it != copy.end(); it++ )
g_FormationManager.AddUnit(*it, m_index);
}
bool CEntityFormation::AddUnit( CEntity* entity )
{
debug_assert( entity );
//Add the unit to the most appropriate slot
for (size_t i=0; i<m_base->m_numSlots; ++i)
{
if ( IsBetterUnit( i, entity ) )
{
if ( m_entities[i] )
{
CEntity* prev = m_entities[i];
RemoveUnit( m_entities[i] );
m_entities[i] = entity;
AddUnit( prev );
}
m_entities[i] = entity;
++m_numEntities;
entity->m_formation = (ssize_t)m_index;
entity->m_formationSlot = (ssize_t)i;
return true;
}
}
return false;
}
void CEntityFormation::RemoveUnit( CEntity* entity )
{
if ( !(IsValidOrder(entity->m_formationSlot) && entity) )
return;
m_entities[entity->m_formationSlot] = NULL;
entity->m_formation = -1;
entity->m_formationSlot = (size_t)-1;
--m_numEntities;
//UpdateFormation();
}
bool CEntityFormation::IsSlotAppropriate( size_t order, CEntity* entity )
{
debug_assert( entity );
if ( !IsValidOrder(order) )
return false;
for ( size_t idx=0; idx < m_base->m_slots[order].category.size(); ++idx )
{
CStr tmp( m_base->m_slots[order].category[idx] );
if ( entity->m_classes.IsMember( tmp ) )
return true;
}
return false;
}
bool CEntityFormation::IsBetterUnit( size_t order, CEntity* entity )
{
if ( !( IsValidOrder(order) && entity ) )
return false;
//Adding to an empty slot, check if we're elligible
if ( !m_entities[order] )
return IsSlotAppropriate(order, entity);
//Go through each category and check both units' classes. Must be done in order
//because categories are listed in order of importance
for ( size_t idx=0; idx < m_base->m_slots[order].category.size(); ++idx )
{
CStr cat = m_base->m_slots[order].category[idx];
bool current=false;
bool newEnt=false;
current = m_entities[order]->m_classes.IsMember( cat );
newEnt = entity->m_classes.IsMember( cat );
if ( current != newEnt )
return newEnt;
}
return false;
}
void CEntityFormation::UpdateFormation()
{
//Get the entities in the right order (as in, ordered correctly and in the right order/slot)
for ( size_t i=1; i<m_base->m_numSlots; ++i )
{
if ( !m_entities[i] )
continue;
//Go through slots with higher order
for ( size_t j=i-1; j != 0; --j )
{
if ( IsBetterUnit( j, m_entities[i] ) )
{
CEntity* temp = m_entities[j];
m_entities[j] = m_entities[i];
m_entities[i] = temp;
ssize_t tmpSlot = m_entities[i]->m_formationSlot;
m_entities[i]->m_formationSlot = m_entities[j]->m_formationSlot;
m_entities[j]->m_formationSlot = tmpSlot;
--i;
}
}
}
CEntityList entities = GetEntityList();
/*
CNetMessage* msg = CNetMessage::CreatePositionMessage( entities, NMT_FORMATION_GOTO, m_position );
g_Game->GetSimulation()->QueueLocalCommand(msg);
*/
}
void CEntityFormation::ResetAllEntities()
{
for ( size_t i=0; i<m_base->m_numSlots; ++i )
m_entities[i] = NULL;
}
void CEntityFormation::ResetAngleDivs()
{
for ( int i=0; i<m_base->m_anglePenaltyDivs; ++i )
m_angleDivs[i] = false;
}
void CEntityFormation::SelectAllUnits() const
{
for ( size_t i=0; i<m_base->m_numSlots; ++i )
{
/*
if ( m_entities[i] && !g_Selection.IsSelected(m_entities[i]->me) )
g_Selection.AddSelection( m_entities[i]->me );
*/
}
}
// (cannot be declared inline in the header due to use of CFormation (m_base)
size_t CEntityFormation::GetSlotCount() const
{
return m_base->m_numSlots;
}
CEntityList CEntityFormation::GetEntityList() const
{
CEntityList ret;
for ( size_t i=0; i<m_base->m_numSlots; i++ )
{
if ( m_entities[i] )
ret.push_back( m_entities[i]->me);
}
return ret;
}
CVector2D CEntityFormation::GetSlotPosition( size_t order ) const
{
if ( IsValidOrder(order) )
return CVector2D ( m_base->m_slots[order].rankOff, m_base->m_slots[order].fileOff );
return CVector2D(-1, -1);
}
void CEntityFormation::BaseToMovement()
{
CFormation* tmp = m_self;
CFormation* move = g_EntityFormationCollection.GetTemplate( m_base->m_movement );
if (!move)
return;
SwitchBase( move );
//SwitchBase resets m_self, so reset it so we can return to the correct base later
m_self = tmp;
}
void CEntityFormation::ResetIndex( size_t index )
{
m_index = index;
for ( size_t i=0; i< m_entities.size(); ++i )
{
if ( m_entities[i] )
m_entities[i]->m_formation = (ssize_t)m_index;
}
}
/*
inline void CEntityFormation::SetDuplication( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* argv )
{
m_duplication=ToPrimitive<bool>( argv[0] );
}
inline bool CEntityFormation::IsDuplication( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
return m_duplication;
}*/

View File

@ -1,115 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
//Instances of this class contain the actual information about in-game formations.
//It is based off of Formation.cpp and uses it as a reference as to what can and cannot
//be done in this formation. This is represented as m_base.
#ifndef INCLUDED_ENTITYFORMATION
#define INCLUDED_ENTITYFORMATION
#include "ps/Vector2D.h"
class CVector2D;
class CEntity;
struct CEntityList;
class CClassSet;
class CFormation;
class CEntityFormation
{
friend class CFormationManager;
public:
CEntityFormation( CFormation*& base, size_t index );
~CEntityFormation();
size_t GetEntityCount() const
{
return m_numEntities;
}
float GetSpeed() const
{
return m_speed;
}
size_t GetSlotCount() const;
CEntityList GetEntityList() const;
CVector2D GetSlotPosition( size_t order ) const;
CVector2D GetPosition() const
{
return m_position;
}
CFormation* GetBase()
{
return m_base;
}
void BaseToMovement();
void SelectAllUnits() const;
inline void SetDuplication( bool duplicate )
{
m_duplication=duplicate;
}
inline bool IsDuplication() const
{
return m_duplication;
}
inline void SetLock( bool lock )
{
m_locked=lock;
}
inline bool IsLocked() const
{
return m_locked;
}
inline bool IsValidOrder(size_t order) const
{
return ( order < GetSlotCount() );
}
private:
size_t m_numEntities;
size_t m_index;
float m_speed; //speed of slowest unit
float m_orientation; //Our orientation angle. Used for rotation.
CVector2D m_position;
bool m_locked;
//Prevents other selected units from reordering the formation after one has already done it.
bool m_duplication;
CFormation* m_base;
CFormation* m_self; //Keeps track of base (referred to during movement switching)
std::vector<CEntity*> m_entities; //number of units currently in this formation
std::vector<bool> m_angleDivs; //attack direction penalty-true=being attacked from sector
std::vector<float> m_angleVals;
bool AddUnit( CEntity* entity );
void RemoveUnit( CEntity* entity );
bool IsSlotAppropriate( size_t order, CEntity* entity ); //If empty, can we use this slot?
bool IsBetterUnit( size_t order, CEntity* entity );
void UpdateFormation();
void SwitchBase( CFormation*& base );
void ResetIndex( size_t index );
void ResetAllEntities(); //Sets all handles to invalid
void ResetAngleDivs();
};
#endif

View File

@ -1,618 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "graphics/GameView.h"
#include "graphics/Model.h"
#include "graphics/Sprite.h"
#include "graphics/Terrain.h"
#include "graphics/Unit.h"
#include "graphics/UnitManager.h"
#include "lib/res/graphics/ogl_tex.h"
#include "maths/MathUtil.h"
#include "maths/scripting/JSInterface_Vector3D.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/Player.h"
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
#include "scripting/ScriptableComplex.inl"
#include "Aura.h"
#include "Collision.h"
#include "Entity.h"
#include "EntityFormation.h"
#include "EntityTemplate.h"
#include "EntityTemplateCollection.h"
#include "EventHandlers.h"
#include "Formation.h"
#include "FormationManager.h"
#include "ProductionQueue.h"
#include "TechnologyCollection.h"
#include "TerritoryManager.h"
#include <algorithm>
void CEntity::Render()
{
if( !m_visible ) return;
if( !m_orderQueue.empty() )
{
std::deque<CEntityOrder>::iterator it;
CBoundingObject* destinationCollisionObject;
float x0, y0, x, y;
x = m_orderQueue.front().m_target_location.x;
y = m_orderQueue.front().m_target_location.y;
for( it = m_orderQueue.begin(); it < m_orderQueue.end(); it++ )
{
if( it->m_type == CEntityOrder::ORDER_PATROL )
break;
x = it->m_target_location.x;
y = it->m_target_location.y;
}
destinationCollisionObject = GetContainingObject( CVector2D( x, y ) );
glShadeModel( GL_FLAT );
glBegin( GL_LINE_STRIP );
glVertex3f( m_position.X, m_position.Y + 0.25f, m_position.Z );
x = m_position.X;
y = m_position.Z;
for( it = m_orderQueue.begin(); it < m_orderQueue.end(); it++ )
{
x0 = x;
y0 = y;
x = it->m_target_location.x;
y = it->m_target_location.y;
rayIntersectionResults r;
CVector2D fwd( x - x0, y - y0 );
float l = fwd.Length();
fwd = fwd.Normalize();
CVector2D rgt = fwd.beta();
if( GetRayIntersection( CVector2D( x0, y0 ), fwd, rgt, l, m_bounds->m_radius, destinationCollisionObject, &r ) )
{
glEnd();
glBegin( GL_LINES );
glColor3f( 1.0f, 0.0f, 0.0f );
glVertex3f( x0 + fwd.x * r.distance, GetAnchorLevel( x0 + fwd.x * r.distance, y0 + fwd.y * r.distance ) + 0.25f, y0 + fwd.y * r.distance );
glVertex3f( r.position.x, GetAnchorLevel( r.position.x, r.position.y ) + 0.25f, r.position.y );
glEnd();
glBegin( GL_LINE_STRIP );
glVertex3f( x0, GetAnchorLevel( x0, y0 ), y0 );
}
switch( it->m_type )
{
case CEntityOrder::ORDER_GOTO:
glColor3f( 1.0f, 0.0f, 0.0f );
break;
case CEntityOrder::ORDER_GOTO_COLLISION:
glColor3f( 1.0f, 0.5f, 0.5f );
break;
case CEntityOrder::ORDER_GOTO_NOPATHING:
case CEntityOrder::ORDER_GOTO_SMOOTHED:
glColor3f( 0.5f, 0.5f, 0.5f );
break;
case CEntityOrder::ORDER_PATROL:
glColor3f( 0.0f, 1.0f, 0.0f );
break;
default:
continue;
}
glVertex3f( x, GetAnchorLevel( x, y ) + 0.25f, y );
}
glEnd();
glShadeModel( GL_SMOOTH );
}
glColor3f( 1.0f, 1.0f, 1.0f );
if( GetCollisionObject( this ) )
glColor3f( 0.5f, 0.5f, 1.0f );
m_bounds->Render( GetAnchorLevel( m_position.X, m_position.Z ) + 0.25f ); //m_position.Y + 0.25f );
}
void CEntity::RenderSelectionOutline( float alpha )
{
if( !m_bounds || !m_visible )
return;
if( GetCollisionObject( m_bounds, m_player, &m_base->m_socket ) )
{
glColor4f( 1.0f, 0.5f, 0.5f, alpha ); // We're colliding with another unit; colour outline pink
}
else
{
const SPlayerColour& col = m_player->GetColour();
glColor3f( col.r, col.g, col.b ); // Colour outline with player colour
}
glBegin( GL_LINE_LOOP );
CVector3D pos = m_graphics_position;
switch( m_bounds->m_type )
{
case CBoundingObject::BOUND_CIRCLE:
{
float radius = ((CBoundingCircle*)m_bounds)->m_radius;
for( int i = 0; i < SELECTION_CIRCLE_POINTS; i++ )
{
float ang = i * 2 * (float)M_PI / SELECTION_CIRCLE_POINTS;
float x = pos.X + radius * sin( ang );
float y = pos.Z + radius * cos( ang );
#ifdef SELECTION_TERRAIN_CONFORMANCE
glVertex3f( x, GetAnchorLevel( x, y ) + 0.25f, y );
#else
glVertex3f( x, pos.Y + 0.25f, y );
#endif
}
break;
}
case CBoundingObject::BOUND_OABB:
{
CVector2D p, q;
CVector2D u, v;
q.x = pos.X;
q.y = pos.Z;
float d = ((CBoundingBox*)m_bounds)->m_d;
float w = ((CBoundingBox*)m_bounds)->m_w;
u.x = sin( m_graphics_orientation.Y );
u.y = cos( m_graphics_orientation.Y );
v.x = u.y;
v.y = -u.x;
#ifdef SELECTION_TERRAIN_CONFORMANCE
for( int i = SELECTION_BOX_POINTS; i > -SELECTION_BOX_POINTS; i-- )
{
p = q + u * d + v * ( w * (float)i / (float)SELECTION_BOX_POINTS );
glVertex3f( p.x, GetAnchorLevel( p.x, p.y ) + 0.25f, p.y );
}
for( int i = SELECTION_BOX_POINTS; i > -SELECTION_BOX_POINTS; i-- )
{
p = q + u * ( d * (float)i / (float)SELECTION_BOX_POINTS ) - v * w;
glVertex3f( p.x, GetAnchorLevel( p.x, p.y ) + 0.25f, p.y );
}
for( int i = -SELECTION_BOX_POINTS; i < SELECTION_BOX_POINTS; i++ )
{
p = q - u * d + v * ( w * (float)i / (float)SELECTION_BOX_POINTS );
glVertex3f( p.x, GetAnchorLevel( p.x, p.y ) + 0.25f, p.y );
}
for( int i = -SELECTION_BOX_POINTS; i < SELECTION_BOX_POINTS; i++ )
{
p = q + u * ( d * (float)i / (float)SELECTION_BOX_POINTS ) + v * w;
glVertex3f( p.x, GetAnchorLevel( p.x, p.y ) + 0.25f, p.y );
}
#else
p = q + u * h + v * w;
glVertex3f( p.x, GetAnchorLevel( p.x, p.y ) + 0.25f, p.y );
p = q + u * h - v * w;
glVertex3f( p.x, GetAnchorLevel( p.x, p.y ) + 0.25f, p.y );
p = q - u * h + v * w;
glVertex3f( p.x, GetAnchorLevel( p.x, p.y ) + 0.25f, p.y );
p = q + u * h + v * w;
glVertex3f( p.x, GetAnchorLevel( p.x, p.y ) + 0.25f, p.y );
#endif
break;
}
}
glEnd();
}
void CEntity::RenderAuras()
{
if( !(m_bounds && m_visible && !m_auras.empty()) )
return;
const SPlayerColour& playerCol = m_player->GetColour();
glPushMatrix();
glTranslatef(m_graphics_position.X, m_graphics_position.Y,
m_graphics_position.Z);
size_t i=0;
for ( AuraTable::iterator it=m_auras.begin(); it!=m_auras.end(); ++it, ++i )
{
CVector4D color = it->second->m_color;
glColor4f(color.m_X, color.m_Y, color.m_Z, color.m_W);
#ifdef SELECTION_TERRAIN_CONFORMANCE
//This starts to break when the radius is bigger
if ( it->second->m_radius < 15.0f )
{
glBegin(GL_TRIANGLE_FAN);
glVertex3f(0.0f, GetAnchorLevel(m_graphics_position.X,
m_graphics_position.Z)-m_graphics_position.Y+.5f, 0.0f);
for ( int j=0; j<AURA_CIRCLE_POINTS; ++j )
{
CVector2D ypos( m_unsnappedPoints[i][j].x+m_graphics_position.X,
m_unsnappedPoints[i][j].y+m_graphics_position.Z );
CVector3D pos( m_unsnappedPoints[i][j].x, GetAnchorLevel(ypos.x, ypos.y)-
m_graphics_position.Y+.5f, m_unsnappedPoints[i][j].y );
glVertex3f(pos.X, pos.Y, pos.Z);
}
//Loop around
CVector3D pos( m_unsnappedPoints[i][0].x,
GetAnchorLevel(m_unsnappedPoints[i][0].x+m_graphics_position.X,
m_unsnappedPoints[i][0].y+m_graphics_position.Z)-
m_graphics_position.Y+.5f, m_unsnappedPoints[i][0].y );
glVertex3f(pos.X, pos.Y, pos.Z);
glEnd(); // GL_TRIANGLE_FAN
}
//Draw edges
glEnable(GL_LINE_SMOOTH);
glLineWidth(1.0f);
glBegin(GL_LINE_LOOP);
glColor3f( playerCol.r, playerCol.g, playerCol.b );
for ( int j=0; j<AURA_CIRCLE_POINTS; ++j )
{
CVector2D ypos( m_unsnappedPoints[i][j].x+m_graphics_position.X,
m_unsnappedPoints[i][j].y+m_graphics_position.Z );
CVector3D pos( m_unsnappedPoints[i][j].x, GetAnchorLevel(ypos.x, ypos.y)-
m_graphics_position.Y+.5f, m_unsnappedPoints[i][j].y );
glVertex3f(pos.X, pos.Y, pos.Z);
}
glEnd();
glDisable(GL_LINE_SMOOTH);
#else
if ( it->second->m_radius < 15.0f )
{
for ( int j=0; j<SELECTION_CIRLCE_POINTS; ++j )
glVertex3f(m_unsnappedPoints[i][j].x, .25f, m_unsnappedPoints[i][j].y);
glVertex3f(m_unsnappedPoints[i][0].x, .25f, m_unsnappedPoints[i][0].y);
}
glEnd();
//Draw edges
glBegin(GL_LINE_LOOP);
glColor3f( col.r, col.g, col.b );
for ( int j=0; j<SELECTION_CIRLCE_POINTS; ++j )
glVertex3f(unsnappedPoints[i][j].x, .25f, m_unsnappedPoints[i][j].y);
glEnd();
#endif
}
glPopMatrix();
}
CVector2D CEntity::GetScreenCoords( float height )
{
CCamera &camera = *g_Game->GetView()->GetCamera();
float sx, sy;
CVector3D above;
above.X = m_position.X;
above.Z = m_position.Z;
above.Y = GetAnchorLevel(m_position.X, m_position.Z) + height;
camera.GetScreenCoordinates(above, sx, sy);
return CVector2D( sx, sy );
}
void CEntity::DrawRect( CVector3D& centre, CVector3D& up, CVector3D& right, float x1, float y1, float x2, float y2 )
{
glBegin(GL_QUADS);
const int X[] = {1,1,0,0}; // which X and Y to choose at each vertex
const int Y[] = {0,1,1,0};
for( int i=0; i<4; i++ )
{
CVector3D vec = centre + right * (X[i] ? x1 : x2) + up * (Y[i] ? y1 : y2);
glTexCoord2f( X[i], Y[i] );
glVertex3fv( &vec.X );
}
glEnd();
}
void CEntity::DrawBar( CVector3D& centre, CVector3D& up, CVector3D& right,
float x1, float y1, float x2, float y2,
SColour col1, SColour col2, float currVal, float maxVal )
{
// Figure out fraction that should be col1
float fraction;
if(maxVal == 0) fraction = 1.0f;
else fraction = clamp( currVal / maxVal, 0.0f, 1.0f );
/*// Draw the border at full size
ogl_tex_bind( g_Selection.m_unitUITextures[m_base->m_barBorder] );
DrawRect( centre, up, right, x1, y1, x2, y2 );
ogl_tex_bind( 0 );
// Make the bar contents slightly smaller than the border
x1 += m_base->m_barBorderSize;
y1 += m_base->m_barBorderSize;
x2 -= m_base->m_barBorderSize;
y2 -= m_base->m_barBorderSize;*/
// Draw the bar contents
float xMid = x2 * fraction + x1 * (1.0f - fraction);
glColor3fv( &col1.r );
DrawRect( centre, up, right, x1, y1, xMid, y2 );
glColor3fv( &col2.r );
DrawRect( centre, up, right, xMid, y1, x2, y2 );
}
void CEntity::RenderBars()
{
if( !m_base->m_barsEnabled || !m_bounds || !m_visible)
return;
SnapToGround();
CVector3D centre = m_graphics_position;
centre.Y += m_base->m_barOffset;
CVector3D up = g_Game->GetView()->GetCamera()->m_Orientation.GetUp();
CVector3D right = -g_Game->GetView()->GetCamera()->m_Orientation.GetLeft();
float w = m_base->m_barWidth;
float h = m_base->m_barHeight;
float borderSize = m_base->m_barBorderSize;
// Draw the health and stamina bars; if the unit has no stamina, the health bar is
// drawn centered, otherwise it's offset slightly up and the stamina bar is offset
// slightly down so that they overlap over an area of size borderSize.
bool hasStamina = (m_staminaMax > 0);
float backgroundW = w+2*borderSize;
float backgroundH = hasStamina ? 2*h+2*borderSize : h+2*borderSize;
/*
ogl_tex_bind( g_Selection.m_unitUITextures[m_base->m_barBorder] );
DrawRect( centre, up, right, -backgroundW/2, -backgroundH/2, backgroundW/2, backgroundH/2 );
*/
ogl_tex_bind( 0 );
float off = hasStamina ? h/2 : 0;
DrawBar( centre, up, right, -w/2, off-h/2, w/2, off+h/2,
SColour(0,1,0), SColour(1,0,0), m_healthCurr, m_healthMax );
if( hasStamina )
{
DrawBar( centre, up, right, -w/2, -h, w/2, 0,
SColour(0,0,1), SColour(0.4f,0.4f,0.1f), m_staminaCurr, m_staminaMax );
}
// Draw the rank icon
/*
CSelectedEntities::MapFilenameToHandle::iterator it = g_Selection.m_unitUITextures.find( m_rankName );
if( it != g_Selection.m_unitUITextures.end() )
{
float size = 2*h + borderSize;
ogl_tex_bind( it->second );
DrawRect( centre, up, right, w/2+borderSize, -size/2, w/2+borderSize+size, size/2 );
ogl_tex_bind( 0 );
}
*/
}
void CEntity::RenderBarBorders()
{
if( !m_visible )
return;
/*
if ( m_base->m_staminaBarHeight >= 0 &&
g_Selection.m_unitUITextures.find(m_base->m_healthBorderName) != g_Selection.m_unitUITextures.end() )
{
ogl_tex_bind( g_Selection.m_unitUITextures[m_base->m_healthBorderName] );
CVector2D pos = GetScreenCoords( m_base->m_healthBarHeight );
float left = pos.x - m_base->m_healthBorderWidth/2;
float right = pos.x + m_base->m_healthBorderWidth/2;
pos.y = g_yres - pos.y;
float bottom = pos.y + m_base->m_healthBorderHeight/2;
float top = pos.y - m_base->m_healthBorderHeight/2;
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f( left, bottom, 0 );
glTexCoord2f(0.0f, 1.0f); glVertex3f( left, top, 0 );
glTexCoord2f(1.0f, 1.0f); glVertex3f( right, top, 0 );
glTexCoord2f(1.0f, 0.0f); glVertex3f( right, bottom, 0 );
glEnd();
}
if ( m_base->m_staminaBarHeight >= 0 &&
g_Selection.m_unitUITextures.find(m_base->m_staminaBorderName) != g_Selection.m_unitUITextures.end() )
{
ogl_tex_bind( g_Selection.m_unitUITextures[m_base->m_staminaBorderName] );
CVector2D pos = GetScreenCoords( m_base->m_staminaBarHeight );
float left = pos.x - m_base->m_staminaBorderWidth/2;
float right = pos.x + m_base->m_staminaBorderWidth/2;
pos.y = g_yres - pos.y;
float bottom = pos.y + m_base->m_staminaBorderHeight/2;
float top = pos.y - m_base->m_staminaBorderHeight/2;
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f( left, bottom, 0 );
glTexCoord2f(0.0f, 1.0f); glVertex3f( left, top, 0 );
glTexCoord2f(1.0f, 1.0f); glVertex3f( right, top, 0 );
glTexCoord2f(1.0f, 0.0f); glVertex3f( right, bottom, 0 );
glEnd();
}
*/
}
void CEntity::RenderHealthBar()
{
if( !m_bounds || !m_visible )
return;
if( m_base->m_healthBarHeight < 0 )
return; // negative bar height means don't display health bar
float fraction;
if(m_healthMax == 0) fraction = 1.0f;
else fraction = clamp(m_healthCurr / m_healthMax, 0.0f, 1.0f);
CVector2D pos = GetScreenCoords( m_base->m_healthBarHeight );
float x1 = pos.x - m_base->m_healthBarSize/2;
float x2 = pos.x + m_base->m_healthBarSize/2;
float y = g_yres - pos.y;
glLineWidth( m_base->m_healthBarWidth );
glBegin(GL_LINES);
// green part of bar
glColor3f( 0, 1, 0 );
glVertex3f( x1, y, 0 );
glColor3f( 0, 1, 0 );
glVertex3f( x1 + m_base->m_healthBarSize*fraction, y, 0 );
// red part of bar
glColor3f( 1, 0, 0 );
glVertex3f( x1 + m_base->m_healthBarSize*fraction, y, 0 );
glColor3f( 1, 0, 0 );
glVertex3f( x2, y, 0 );
glEnd();
glLineWidth(1.0f);
}
void CEntity::RenderStaminaBar()
{
if( !m_bounds || !m_visible )
return;
if( m_base->m_staminaBarHeight < 0 )
return; // negative bar height means don't display stamina bar
float fraction;
if(m_staminaMax == 0) fraction = 1.0f;
else fraction = clamp(m_staminaCurr / m_staminaMax, 0.0f, 1.0f);
CVector2D pos = GetScreenCoords( m_base->m_staminaBarHeight );
float x1 = pos.x - m_base->m_staminaBarSize/2;
float x2 = pos.x + m_base->m_staminaBarSize/2;
float y = g_yres - pos.y;
glLineWidth( m_base->m_staminaBarWidth );
glBegin(GL_LINES);
// blue part of bar
glColor3f( 0.1f, 0.1f, 1 );
glVertex3f( x1, y, 0 );
glColor3f( 0.1f, 0.1f, 1 );
glVertex3f( x1 + m_base->m_staminaBarSize*fraction, y, 0 );
// purple part of bar
glColor3f( 0.3f, 0, 0.3f );
glVertex3f( x1 + m_base->m_staminaBarSize*fraction, y, 0 );
glColor3f( 0.3f, 0, 0.3f );
glVertex3f( x2, y, 0 );
glEnd();
glLineWidth(1.0f);
}
void CEntity::RenderRank()
{
if( !m_bounds || !m_visible )
return;
if( m_base->m_rankHeight < 0 )
return; // negative height means don't display rank
//Check for valid texture
/*
if( g_Selection.m_unitUITextures.find( m_rankName ) == g_Selection.m_unitUITextures.end() )
return;
*/
CCamera *camera = g_Game->GetView()->GetCamera();
float sx, sy;
CVector3D above;
above.X = m_position.X;
above.Z = m_position.Z;
above.Y = GetAnchorLevel(m_position.X, m_position.Z) + m_base->m_rankHeight;
camera->GetScreenCoordinates(above, sx, sy);
int size = m_base->m_rankWidth/2;
float x1 = sx + m_base->m_healthBarSize/2;
float x2 = sx + m_base->m_healthBarSize/2 + 2*size;
float y1 = g_yres - (sy - size); //top
float y2 = g_yres - (sy + size); //bottom
// ogl_tex_bind(g_Selection.m_unitUITextures[m_rankName]);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
glBegin(GL_QUADS);
glTexCoord2f(1.0f, 0.0f); glVertex3f( x2, y2, 0 );
glTexCoord2f(1.0f, 1.0f); glVertex3f( x2, y1, 0 );
glTexCoord2f(0.0f, 1.0f); glVertex3f( x1, y1, 0 );
glTexCoord2f(0.0f, 0.0f); glVertex3f( x1, y2, 0 );
glEnd();
}
void CEntity::RenderRallyPoint()
{
if( !m_visible )
return;
/*
if ( !entf_get(ENTF_HAS_RALLY_POINT) ||
g_Selection.m_unitUITextures.find(m_base->m_rallyName) == g_Selection.m_unitUITextures.end() )
{
return;
}
*/
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
CSprite sprite;
CTexture tex;
// tex.SetHandle( g_Selection.m_unitUITextures[m_base->m_rallyName] );
sprite.SetTexture(&tex);
// Place the sprite slightly above ground/water level.
float terrainHeight = g_Game->GetWorld()->GetTerrain()->GetExactGroundLevel(m_rallyPoint);
float height = std::max(terrainHeight, g_Renderer.GetWaterManager()->m_WaterHeight) + 0.5f;
CVector3D rally(m_rallyPoint.x, height, m_rallyPoint.y);
sprite.SetTranslation(rally);
sprite.Render();
}

View File

@ -1,767 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// Entity state-machine processing code.
#include "precompiled.h"
#include "Entity.h"
#include "EntityTemplate.h"
#include "EventHandlers.h"
#include "graphics/Model.h"
#include "graphics/ObjectEntry.h"
#include "graphics/SkeletonAnim.h"
#include "graphics/SkeletonAnimDef.h" // Animation duration
#include "graphics/Unit.h"
#include "ProductionQueue.h"
#include "maths/MathUtil.h"
#include "Collision.h"
#include "LOSManager.h"
#include "graphics/Terrain.h"
#include "Stance.h"
//#include "sound/SoundGroupMgr.h"
#include "ps/Game.h"
#include "ps/World.h"
#include "lib/rand.h"
#include "ps/GameSetup/Config.h"
enum EGotoSituation
{
NORMAL = 0,
ALREADY_AT_DESTINATION,
REACHED_DESTINATION,
COLLISION_WITH_DESTINATION,
COLLISION_NEAR_DESTINATION,
COLLISION_OVERLAPPING_OBJECTS,
COLLISION_OTHER,
WOULD_LEAVE_MAP,
STANCE_DISALLOWS
};
bool CEntity::ShouldRun(float distance)
{
if( !entf_get(ENTF_SHOULD_RUN) )
return false;
// tired
if( m_staminaCurr <= 0 )
return false;
if( distance >= m_runMaxRange )
return false;
// don't start running if less than minimum
if( distance <= m_runMinRange && !entf_get(ENTF_IS_RUNNING) )
return false;
return true;
}
float CEntity::ChooseMovementSpeed( float distance )
{
bool should_run = ShouldRun(distance);
float speed = should_run? m_runSpeed : m_speed;
const char* anim_name = should_run? "run" : "walk";
// Modify the speed based on the slope of the terrain in our direction (obtained from our x orientation)
float angle = m_orientation_unclamped.x;
int sector = rintf( angle / ((float)M_PI/2) * m_base->m_pitchDivs );
speed -= sector * m_base->m_pitchValue;
entf_set_to(ENTF_IS_RUNNING, should_run);
if ( m_actor )
{
if ( !m_actor->IsPlayingAnimation( anim_name ) )
{
m_actor->SetAnimationState( anim_name, false, speed, 0.f, false, L"" );
// Animation desync
m_actor->GetModel().Update( rand( 0, 1000 ) / 1000.0f );
}
}
return speed;
}
// Does all the shared processing for line-of-sight gotos
int CEntity::ProcessGotoHelper( CEntityOrder* current, int timestep_millis, HEntity& collide, float& timeLeft )
{
float timestep=timestep_millis/1000.0f;
CVector2D delta;
delta.x = (float)current->m_target_location.x - m_position.X;
delta.y = (float)current->m_target_location.y - m_position.Z;
float len = delta.Length();
if( len < 0.01f )
return( ALREADY_AT_DESTINATION );
// Curve smoothing.
// Here there be trig.
float speed = ChooseMovementSpeed( len );
float scale = speed * timestep;
// Note: Easy optimization: flag somewhere that this unit
// is already pointing the way, and don't do this
// trig every time.right
m_targetorientation = atan2( delta.x, delta.y );
float deltatheta = m_targetorientation - (float)m_orientation.Y;
while( deltatheta > (float)M_PI ) deltatheta -= 2 * (float)M_PI;
while( deltatheta < -(float)M_PI ) deltatheta += 2 * (float)M_PI;
if( fabs( deltatheta ) > 0.01f )
{
if ( m_turningRadius != 0 )
{
float maxTurningSpeed = ( speed / m_turningRadius ) * timestep;
deltatheta = clamp( deltatheta, -maxTurningSpeed, maxTurningSpeed );
}
m_orientation.Y = m_orientation.Y + deltatheta;
m_ahead.x = sin( m_orientation.Y );
m_ahead.y = cos( m_orientation.Y );
// We can only really attempt to smooth paths the pathfinder
// has flagged for us. If the turning-radius calculations are
// applied to other types of waypoint, weirdness happens.
// Things like an entity trying to walk to a point inside
// his turning radius (which he can't do directly, so he'll
// orbit the point indefinitely), or just massive deviations
// making the paths we calculate useless.
// It's also painful trying to watch two entities resolve their
// collision when they're both bound by turning constraints.
// So, as a compromise for the look of the thing, we'll just turn in
// place until we're looking the right way. At least, that's what
// seems logical. But in most cases that looks worse. So actually,
// let's not.
if( current->m_type != CEntityOrder::ORDER_GOTO_SMOOTHED )
m_orientation.Y = m_targetorientation;
}
else
{
m_ahead = delta / len;
m_orientation.Y = m_targetorientation;
}
UpdateXZOrientation();
if( m_bounds && m_bounds->m_type == CBoundingObject::BOUND_OABB )
((CBoundingBox*) m_bounds)->SetOrientation( m_ahead );
EGotoSituation rc = NORMAL;
if( scale > len )
{
// Reached destination. Calculate how much time we have left for the next order.
scale = len;
timeLeft = timestep - (len / speed);
rc = REACHED_DESTINATION;
}
delta = m_ahead * scale;
// What would happen if we moved forward a little?
m_position.X += delta.x;
m_position.Z += delta.y;
if( m_bounds )
{
m_bounds->SetPosition( m_position.X, m_position.Z );
// For now, ignore passThroughAllies for low-level movement (but leave it on for long-range
// pathfinding); ideally we will enable pass-through-allies only for the long-range pathing
// and when the unit is moving to assume its place in a formation, since it looks bad to have
// units stand on each other otherwise.
collide = GetCollisionObject( this, false );
if( collide )
{
// We'd hit something. Let's not.
m_position.X -= delta.x;
m_position.Z -= delta.y;
m_bounds->m_pos -= delta;
// Is it too late to avoid the collision?
if( collide->m_bounds->Intersects( m_bounds ) )
{
// Yes. Oh dear. That can't be good.
// This really shouldn't happen in the current build.
//debug_assert( false && "Overlapping objects" );
// Erm... do nothing?
return( COLLISION_OVERLAPPING_OBJECTS );
}
// No. Is our destination within the obstacle?
if( collide->m_bounds->Contains( current->m_target_location ) )
return( COLLISION_WITH_DESTINATION );
// No. Are we nearing our destination, do we wish to stop there, and is it obstructed?
if( ( m_orderQueue.size() == 1 ) && ( len <= 10.0f ) )
{
CBoundingCircle destinationObs( current->m_target_location.x, current->m_target_location.y, m_bounds->m_radius, 0.0f );
if( GetCollisionObject( &destinationObs ) )
{
// Yes. (Chances are a bunch of units were tasked to the same destination)
return( COLLISION_NEAR_DESTINATION );
}
}
// No?
return( COLLISION_OTHER );
}
}
// Will we step off the map?
if( !g_Game->GetWorld()->GetTerrain()->IsOnMap( m_position.X, m_position.Z ) )
{
// Yes. That's not a particularly good idea, either.
m_position.X -= delta.x;
m_position.Z -= delta.y;
if( m_bounds )
m_bounds->SetPosition( m_position.X, m_position.Z );
// All things being equal, we should only get here while on a collision path
// (No destination should be off the map)
return( WOULD_LEAVE_MAP );
}
// Does our stance not allow us to go there?
if( current->m_source==CEntityOrder::SOURCE_UNIT_AI && !m_stance->CheckMovement( m_position ) )
{
m_position.X -= delta.x;
m_position.Z -= delta.y;
if( m_bounds )
m_bounds->SetPosition( m_position.X, m_position.Z );
return( STANCE_DISALLOWS );
}
// No. I suppose it's OK to go there, then. *disappointed*
return( rc );
}
// Go towards a waypoint, or repath if there is an obstacle in the way
bool CEntity::ProcessGotoNoPathing( CEntityOrder* current, int timestep_millis )
{
HEntity collide;
float timeLeft;
switch( ProcessGotoHelper( current, timestep_millis, collide, timeLeft ) )
{
case ALREADY_AT_DESTINATION:
{
// If on a collision path; decide where to go next. Otherwise, proceed to the next waypoint.
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
Repath();
else
m_orderQueue.pop_front();
return( false );
}
case REACHED_DESTINATION:
{
// Start along the next segment of the path, if one exists
m_orderQueue.pop_front();
if( !m_orderQueue.empty() )
{
CEntityOrder* newOrder = &m_orderQueue.front();
switch( newOrder->m_type )
{
case CEntityOrder::ORDER_GOTO_NOPATHING:
case CEntityOrder::ORDER_GOTO_COLLISION:
case CEntityOrder::ORDER_GOTO_SMOOTHED:
{
int newTimestep = int(timeLeft * 1000.0f);
return( ProcessGotoNoPathing( newOrder, newTimestep ) );
}
}
}
return( false );
}
case COLLISION_OVERLAPPING_OBJECTS:
{
return( false );
}
case COLLISION_WITH_DESTINATION:
{
// We're as close as we can get...
m_orderQueue.pop_front();
return( false );
}
case COLLISION_NEAR_DESTINATION:
{
// Here's a weird idea: (I hope it works)
// Spiral round the destination until a free point is found.
CBoundingCircle destinationObs( current->m_target_location.x, current->m_target_location.y, m_bounds->m_radius, 0.0f );
float interval = destinationObs.m_radius;
float r = interval, theta = 0.0f, delta;
float _x = current->m_target_location.x, _y = current->m_target_location.y;
while( true )
{
delta = interval / r;
theta += delta;
r += ( interval * delta ) / ( 2 * (float)M_PI );
destinationObs.SetPosition( _x + r * cosf( theta ), _y + r * sinf( theta ) );
if( !GetCollisionObject( &destinationObs ) ) break;
}
// Reset our destination
current->m_target_location.x = _x + r * cosf( theta );
current->m_target_location.y = _y + r * sinf( theta );
return( false );
}
case COLLISION_OTHER:
{
// Path around it.
CEntityOrder avoidance;
avoidance.m_type = CEntityOrder::ORDER_GOTO_COLLISION;
CVector2D right;
right.x = m_ahead.y; right.y = -m_ahead.x;
CVector2D avoidancePosition;
// Which is the shortest diversion, going left or right?
// (Weight a little towards the right, to stop both units dodging the same way)
if( ( collide->m_bounds->m_pos - m_bounds->m_pos ).Dot( right ) < 1 )
{
// Turn right.
avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f );
}
else
{
// Turn left.
avoidancePosition = collide->m_bounds->m_pos - right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f );
}
// Create a short path representing this detour
avoidance.m_target_location = avoidancePosition;
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
m_orderQueue.pop_front();
m_orderQueue.push_front( avoidance );
return( false );
}
case WOULD_LEAVE_MAP:
{
// Just stop here, Repath if necessary.
m_orderQueue.pop_front();
return( false );
}
case STANCE_DISALLOWS:
{
return( false ); // The stance will have cleared our order queue already
}
default:
return( false );
}
}
// Go towards a waypoint, unless you can perform a contact action
bool CEntity::ProcessGotoNoPathingContact( CEntityOrder* current, int timestep_millis )
{
CEntityOrder* contact_order = 0;
CEntityOrders::iterator it = m_orderQueue.begin();
for(; it != m_orderQueue.end(); ++it)
{
if( it->m_type == CEntityOrder::ORDER_CONTACT_ACTION
|| it->m_type == CEntityOrder::ORDER_CONTACT_ACTION_NOPATHING )
{
contact_order = &(*it);
break;
}
}
if( ProcessContactAction(contact_order, timestep_millis, false ) )
{
// We are in range to do our action; pop off all the pathing orders
while( m_orderQueue.front().m_type != CEntityOrder::ORDER_CONTACT_ACTION
&& m_orderQueue.front().m_type != CEntityOrder::ORDER_CONTACT_ACTION_NOPATHING )
{
m_orderQueue.pop_front();
}
return true;
}
else
{
return ProcessGotoNoPathing( current, timestep_millis );
}
}
// Handles processing common to (at the moment) gather and melee attack actions
bool CEntity::ProcessContactAction( CEntityOrder* current,
int UNUSED(timestep_millis),
bool repath_if_needed )
{
if( m_actions.find( current->m_action ) == m_actions.end() )
{
return false; // we've been tasked as part of a group but we can't do this action
}
SEntityAction* action = &m_actions[current->m_action];
CEntityOrder::EOrderType transition = CEntityOrder::ORDER_CONTACT_ACTION_NOPATHING;
HEntity target = current->m_target_entity;
if( !target || !target->m_extant )
{
PopOrder();
if( m_orderQueue.empty() && target.IsValid() )
{
CEventTargetExhausted evt( target, action->m_Id );
DispatchEvent( &evt );
}
return false;
}
if( current->m_source != CEntityOrder::SOURCE_TRIGGERS &&
g_Game->GetWorld()->GetLOSManager()->GetUnitStatus( target, m_player ) == UNIT_HIDDEN )
{
PopOrder();
return false;
}
current->m_target_location = target->m_position;
float Distance = Distance2D(current->m_target_location);
if( Distance < action->m_MaxRange )
{
current->m_type = transition;
entf_clear(ENTF_IS_RUNNING);
return true;
}
else if (repath_if_needed)
{
if( current->m_source == CEntityOrder::SOURCE_UNIT_AI && !m_stance->AllowsMovement() )
{
PopOrder();
return false; // We're not allowed to move at all by the current stance
}
ChooseMovementSpeed( Distance );
// The pathfinder will push its result back into this unit's queue and
// add back the current order at the end with the transition type.
current->m_type = transition;
// g_Pathfinder.RequestContactPath( me, current, action->m_MaxRange );
return true;
}
else
{
return false;
}
}
bool CEntity::ProcessContactActionNoPathing( CEntityOrder* current, int timestep_millis )
{
if( m_actions.find( current->m_action ) == m_actions.end() )
{
return false; // we've been tasked as part of a group but we can't do this action
}
if( !m_actor )
{
return( false ); // shouldn't happen, but having no actor would mean we have no animation
}
CEventContactAction contactEvent( current->m_target_entity, current->m_action );
SEntityAction* action = &m_actions[current->m_action];
CStr& animation = action->m_Animation;
HEntity target = current->m_target_entity;
if( m_fsm_cyclepos != NOT_IN_CYCLE )
{
int nextpos = m_fsm_cyclepos + timestep_millis * 2;
if( ( m_fsm_cyclepos <= action->m_Speed ) && ( nextpos > action->m_Speed ) )
{
// const size_t soundGroupIndex = m_base->m_SoundGroupTable[animation];
// g_soundGroupMgr->PlayNext(soundGroupIndex, m_position);
if(!DispatchEvent( &contactEvent ))
{
// Cancel current order
entf_clear(ENTF_IS_RUNNING);
entf_clear(ENTF_SHOULD_RUN);
m_actor->SetAnimationState( "idle", false, 1.f, 0.f, false, L"" );
PopOrder();
if( m_orderQueue.empty() && target.IsValid() )
{
CEventTargetExhausted evt( target, action->m_Id );
DispatchEvent( &evt );
}
return( false );
}
}
if( nextpos >= ( action->m_Speed * 2 ) )
{
// End of cycle.
m_fsm_cyclepos = NOT_IN_CYCLE;
return( false );
}
// Otherwise, increment position.
m_fsm_cyclepos = nextpos;
return( false );
}
// Target's dead (or exhausted), or we cancelled? Then our work here is done.
if( !target || !target->m_extant
|| g_Game->GetWorld()->GetLOSManager()->GetUnitStatus( target, m_player ) == UNIT_HIDDEN )
{
PopOrder();
if( m_orderQueue.empty() && target.IsValid() )
{
CEventTargetExhausted evt( target, action->m_Id );
DispatchEvent( &evt );
}
entf_clear(ENTF_IS_RUNNING);
entf_clear(ENTF_SHOULD_RUN);
return( false );
}
CVector2D delta = CVector2D(target->m_position) - CVector2D(m_position);
float deltaLength = delta.Length();
float adjRange = action->m_MaxRange + m_bounds->m_radius + target->m_bounds->m_radius;
if( action->m_MinRange > 0.0f )
{
float adjMinRange = action->m_MinRange + m_bounds->m_radius + target->m_bounds->m_radius;
if( delta.within( adjMinRange ) )
{
// Too close... avoid it if allowed by the current stance.
if( current->m_source == CEntityOrder::SOURCE_UNIT_AI && !m_stance->AllowsMovement() )
{
PopOrder();
m_actor->SetAnimationState( "idle", false, 1.f, 0.f, false, L"" );
return false; // We're not allowed to move at all by the current stance
}
entf_set(ENTF_SHOULD_RUN);
ChooseMovementSpeed( action->m_MinRange );
// The pathfinder will push its result in front of the current order
// if( !g_Pathfinder.RequestAvoidPath( me, current, action->m_MinRange + 2.0f ) )
// {
// m_actor->SetAnimationState( "idle", false, 1.f, 0.f, false, L"" ); // Nothing we can do.. maybe we'll find a better target
// PopOrder();
// }
return false;
}
}
if( !delta.within( adjRange ) )
{
// Too far away at the moment, chase after the target if allowed...
if( current->m_source == CEntityOrder::SOURCE_UNIT_AI && !m_stance->AllowsMovement() )
{
PopOrder();
return false;
}
// We're aiming to end up at a location just inside our maximum range
// (is this good enough?)
delta = delta.Normalize() * ( adjRange - m_bounds->m_radius );
ChooseMovementSpeed(deltaLength);
current->m_target_location = (CVector2D)target->m_position - delta;
HEntity collide;
float timeLeft;
switch( ProcessGotoHelper( current, timestep_millis, collide, timeLeft ) )
{
case REACHED_DESTINATION:
case ALREADY_AT_DESTINATION:
case COLLISION_WITH_DESTINATION:
case WOULD_LEAVE_MAP:
case STANCE_DISALLOWS:
// Not too far any more...
break;
case NORMAL:
// May or may not be close enough, check...
// (Assuming the delta above will never take us within minimum range)
delta = target->m_position - m_position;
if( delta.within( adjRange ) )
break;
// Otherwise, continue chasing
return( false );
default:
// We have a collision. Path around it.
CEntityOrder avoidance;
avoidance.m_type = CEntityOrder::ORDER_GOTO_COLLISION;
CVector2D right;
right.x = m_ahead.y; right.y = -m_ahead.x;
CVector2D avoidancePosition;
// Which is the shortest diversion, going left or right?
// (Weight a little towards the right, to stop both units dodging the same way)
if( ( collide->m_bounds->m_pos - m_bounds->m_pos ).Dot( right ) < 1 )
{
// Turn right.
avoidancePosition = collide->m_bounds->m_pos + right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f );
}
else
{
// Turn left.
avoidancePosition = collide->m_bounds->m_pos - right * ( collide->m_bounds->m_radius + m_bounds->m_radius * 2.5f );
}
// Create a short path representing this detour
avoidance.m_target_location = avoidancePosition;
if( current->m_type == CEntityOrder::ORDER_GOTO_COLLISION )
m_orderQueue.pop_front();
m_orderQueue.push_front( avoidance );
return( false );
}
}
else
{
// Close enough, but turn to face them.
m_orientation.Y = atan2( delta.x, delta.y );
m_ahead = delta.Normalize();
entf_clear(ENTF_IS_RUNNING);
}
m_actor->SetAnimationState( animation, false, 1000.f / (float)action->m_Speed, 0.f, false, L"" );
// m_actor->SetAnimationSync( (float)( action->m_Speed / 2) / 1000.f );
m_fsm_cyclepos = 0;
return( false );
}
bool CEntity::ProcessGoto( CEntityOrder* current, int UNUSED(timestep_millis) )
{
CVector2D pos( m_position.X, m_position.Z );
CVector2D path_to = current->m_target_location;
float Distance = ( path_to - pos ).Length();
CEntityOrder::EOrderSource source = current->m_source;
m_orderQueue.pop_front();
// pop_front may delete 'current', so we mustn't use it after this point
// Let's just check we're going somewhere...
if( Distance < 0.1f )
{
//entf_clear(ENTF_IS_RUNNING);
//entf_clear(ENTF_SHOULD_RUN);
return( false );
}
ChooseMovementSpeed( Distance );
// The pathfinder will push its result back into this unit's queue.
// g_Pathfinder.RequestPath( me, path_to, source );
return( true );
}
bool CEntity::ProcessGotoWaypoint( CEntityOrder* current, int UNUSED(timestep_milli), bool contact )
{
CVector2D pos( m_position.X, m_position.Z );
CVector2D path_to = current->m_target_location;
float Distance = ( path_to - pos ).Length();
CEntityOrder::EOrderSource source = current->m_source;
float pathfinder_radius = current->m_pathfinder_radius;
m_orderQueue.pop_front();
// pop_front may delete 'current', so we mustn't use it after this point
// Let's just check we're going somewhere...
if( Distance < 0.1f )
{
entf_clear(ENTF_IS_RUNNING);
//entf_clear(ENTF_SHOULD_RUN);
return( false );
}
ChooseMovementSpeed( Distance );
#ifdef USE_DCDT
//Kai: invoking triangulation or original A* pathfinding
if(g_TriPathfind)
{
g_Pathfinder.RequestTriangulationPath( me, path_to, contact, pathfinder_radius, source );
}
else
#endif // USE_DCDT
{
// g_Pathfinder.RequestLowLevelPath( me, path_to, contact, pathfinder_radius, source );
}
return( true );
}
bool CEntity::ProcessPatrol( CEntityOrder* current, int UNUSED(timestep_millis) )
{
CEntityOrder this_segment;
CEntityOrder repeat_patrol;
// Duplicate the patrol order, push one copy onto the start of our order queue
// (that's the path we'll be taking next) and one copy onto the end of the
// queue (to keep us patrolling)
this_segment.m_type = CEntityOrder::ORDER_GOTO;
this_segment.m_pathfinder_radius = current->m_pathfinder_radius;
repeat_patrol.m_type = CEntityOrder::ORDER_PATROL;
repeat_patrol.m_pathfinder_radius = current->m_pathfinder_radius;
m_orderQueue.pop_front();
m_orderQueue.push_front( this_segment );
m_orderQueue.push_back( repeat_patrol );
return( true );
}
bool CEntity::ProcessProduce( CEntityOrder* order )
{
CEventStartProduction evt( order->m_produce_type, order->m_name );
if( DispatchEvent( &evt ) && evt.GetTime() >= 0 )
{
m_productionQueue->AddItem( order->m_produce_type, order->m_name, evt.GetTime() );
}
return( false );
}

View File

@ -1,226 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "Formation.h"
#include "ps/CLogger.h"
#include "ps/CStr.h"
#include "maths/MathUtil.h"
#define LOG_CATEGORY L"Formation"
CFormation::CFormation()
{
m_numSlots = 0;
}
bool CFormation::LoadXml(const VfsPath& filename)
{
CXeromyces XeroFile;
if (XeroFile.Load(filename) != PSRETURN_OK)
return false;
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
EL(formation);
EL(fl);
EL(rk);
EL(blank);
AT(tag);
AT(bonus);
AT(bonusbase);
AT(bonustype);
AT(bonusval);
AT(penalty);
AT(penaltybase);
AT(penaltytype);
AT(penaltyval);
AT(anglepenalty);
AT(anglepenaltydivs);
AT(anglepenaltytype);
AT(anglepenaltyval);
AT(required);
AT(next);
AT(prior);
AT(movement);
AT(filespacing);
AT(rankspacing);
AT(category);
AT(order);
#undef AT
#undef EL
XMBElement Root = XeroFile.GetRoot();
if( Root.GetNodeName() != el_formation )
{
LOG(CLogger::Error, LOG_CATEGORY, L"CFormation::LoadXml: XML root was not \"Formation\" in file %ls. Load failed.", filename.string().c_str() );
return( false );
}
//Load in single attributes
XMBAttributeList Attributes = Root.GetAttributes();
for ( int i=0; i<Attributes.Count; ++i )
{
XMBAttribute Attr = Attributes.Item(i);
if ( Attr.Name == at_tag )
m_tag = CStr(Attr.Value);
else if ( Attr.Name == at_bonus )
m_bonus = CStr(Attr.Value);
else if ( Attr.Name == at_bonusbase )
m_bonusBase = CStr(Attr.Value);
else if ( Attr.Name == at_bonustype )
m_bonusType = CStr(Attr.Value);
else if ( Attr.Name == at_bonusval )
m_bonusVal = CStr(Attr.Value).ToFloat();
else if ( Attr.Name == at_penalty )
m_penalty = CStr(Attr.Value);
else if ( Attr.Name == at_penaltybase )
m_penaltyBase = CStr(Attr.Value);
else if ( Attr.Name == at_penaltytype )
m_penaltyType = CStr(Attr.Value);
else if ( Attr.Name == at_penaltyval )
m_penaltyVal = CStr(Attr.Value).ToFloat();
else if ( Attr.Name == at_anglepenalty )
m_anglePenalty = CStr(Attr.Value);
else if ( Attr.Name == at_anglepenaltydivs )
m_anglePenaltyDivs = CStr(Attr.Value).ToInt();
else if ( Attr.Name == at_anglepenaltytype )
m_anglePenaltyType = CStr(Attr.Value);
else if ( Attr.Name == at_anglepenaltyval )
m_anglePenaltyVal = CStr(Attr.Value).ToFloat();
else if ( Attr.Name == at_required)
m_required = CStr(Attr.Value).ToInt();
else if ( Attr.Name == at_next )
m_next = CStr(Attr.Value);
else if ( Attr.Name == at_prior )
m_prior = CStr(Attr.Value);
else if ( Attr.Name == at_movement )
m_movement = CStr(Attr.Value);
else if ( Attr.Name == at_rankspacing )
m_rankSpacing = CStr(Attr.Value).ToFloat();
else if ( Attr.Name == at_filespacing )
m_fileSpacing = CStr(Attr.Value).ToFloat();
else
{
LOG(CLogger::Error, LOG_CATEGORY, L"CFormation::LoadXml: Invalid attribute %hs defined in formation file %ls. Load failed.", XeroFile.GetAttributeString(Attr.Name).c_str(), filename.string().c_str() );
return( false );
}
}
XMBElementList RootChildren = Root.GetChildNodes();
size_t file=0;
size_t rank=0;
size_t maxrank=0;
//Read in files and ranks
for (int i = 0; i < RootChildren.Count; ++i)
{
XMBElement RootChild = RootChildren.Item(i);
int ChildName = RootChild.GetNodeName();
if ( ChildName == el_fl )
{
rank = 0;
XMBAttributeList FileAttribList = RootChild.GetAttributes();
//Load default category
CStr FileCatValue = FileAttribList.GetNamedItem(at_category);
//Specific slots in this file (row)
XMBElementList RankNodes = RootChild.GetChildNodes();
for ( int r=0; r<RankNodes.Count; ++r )
{
XMBElement Rank = RankNodes.Item(r);
if ( Rank.GetNodeName() == el_blank )
{
++rank;
continue;
}
else if ( Rank.GetNodeName() != el_rk )
return false;
//error
XMBAttributeList RankAttribList = Rank.GetAttributes();
size_t order = CStr( RankAttribList.GetNamedItem(at_order) ).ToInt();
CStr category = CStr( RankAttribList.GetNamedItem(at_category) );
if( order <= 0 )
{
LOG(CLogger::Error, LOG_CATEGORY, L"CFormation::LoadXml: Invalid (negative number or 0) order defined in formation file %ls. The game will try to continue anyway.", filename.string().c_str() );
continue;
}
--order; //We need this to be in line with arrays, so start at 0
//if ( category.Length() )
//AssignCategory(order, category);
//else
AssignCategory(order, FileCatValue);
m_slots[order].fileOff = file * m_fileSpacing;
m_slots[order].rankOff = rank * m_rankSpacing;
++m_numSlots;
++rank;
}
if ( rank > maxrank )
maxrank = rank;
++file;
} //if el_fl
else if ( ChildName == el_blank )
++file;
}
float centerx = maxrank * m_rankSpacing / 2.0f;
float centery = file * m_fileSpacing / 2.0f;
//Here we check to make sure no order was skipped over. If so, failure, because we rely
//on a linearly accessible slots in entityformation.cpp.
for ( size_t i=0; i<m_numSlots; ++i )
{
if ( m_slots.find(i) == m_slots.end() )
{
LOG(CLogger::Error, LOG_CATEGORY, L"CFormation::LoadXml: Missing orders in %ls. Load failed.", filename.string().c_str() );
return false;
}
else
{
m_slots[i].rankOff = m_slots[i].rankOff - centerx;
m_slots[i].fileOff = m_slots[i].fileOff - centery;
}
}
return true;
}
void CFormation::AssignCategory(size_t order, const CStr& category_)
{
CStr category(category_);
category.Remove( CStr(",") );
category = category + " "; //So the final word will be pushed as well
CStr temp;
//Push categories until last space
while ( ( temp = category.BeforeFirst(" ") ) != "" )
{
m_slots[order].category.push_back(temp);
//Don't use remove because certain categories could be substrings of others
size_t off = category.find(temp);
category.erase( off, temp.length() );
}
}

View File

@ -1,96 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
//This class loads all the formation data needs to create an instance of a particular formation.
#ifndef INCLUDED_FORMATION
#define INCLUDED_FORMATION
#include <vector>
#include <map>
#include "ps/XML/Xeromyces.h"
#include "ps/CStr.h"
class CFormation
{
friend class CFormationManager;
friend class CFormationCollection;
friend class CEntityFormation;
struct FormationSlot
{
float fileOff; //Distance between files of this slot and the formation's center
float rankOff;
std::vector<CStr> category;
};
public:
CFormation();
~CFormation(){}
const CStr& GetBonus(){ return m_bonus; }
const CStr& GetBonusBase(){ return m_bonusBase; }
const CStr& GetBonusType(){ return m_bonusType; }
float GetBonusVal(){ return m_bonusVal; }
const CStr& GetPenalty(){ return m_penalty; }
const CStr& GetPenaltyBase(){ return m_penaltyBase; }
const CStr& GetPenaltyType(){ return m_penaltyType; }
float GetPenaltyVal(){ return m_penaltyVal; }
const CStr& GetAnglePenalty(){ return m_anglePenalty; }
const CStr& GetAnglePenaltyType(){ return m_anglePenaltyType; }
int GetAnglePenaltyDivs(){ return m_anglePenaltyDivs; }
float GetAnglePenaltyVal(){ return m_anglePenaltyVal; }
private:
CStr m_tag;
CStr m_bonus;
CStr m_bonusBase;
CStr m_bonusType;
float m_bonusVal;
CStr m_penalty;
CStr m_penaltyBase;
CStr m_penaltyType;
float m_penaltyVal;
CStr m_anglePenalty;
int m_anglePenaltyDivs;
CStr m_anglePenaltyType;
float m_anglePenaltyVal;
size_t m_required;
CStr m_next;
CStr m_prior;
CStr m_movement;
float m_fileSpacing;
float m_rankSpacing;
size_t m_numSlots; //how many possible slots in this formation
size_t m_numRanks;
size_t m_numFiles;
//The key is the "order" of the slot
std::map<size_t, FormationSlot> m_slots;
bool LoadXml(const VfsPath& filename);
void AssignCategory(size_t order, const CStr& category); //takes care of formatting strings
};
#endif

View File

@ -1,92 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "FormationCollection.h"
#include "graphics/ObjectManager.h"
#include "graphics/Model.h"
#include "ps/CLogger.h"
#define LOG_CATEGORY L"formation"
void CFormationCollection::LoadFile( const VfsPath& pathname )
{
// Build the formation name -> filename mapping. This is done so that
// the formation 'x' can be in units/x.xml, structures/x.xml, etc, and
// we don't have to search every directory for x.xml.
const CStrW basename(fs::basename(pathname));
m_templateFilenames[basename] = pathname;
}
static LibError LoadFormationThunk( const VfsPath& path, const FileInfo& UNUSED(fileInfo), uintptr_t cbData )
{
CFormationCollection* this_ = (CFormationCollection*)cbData;
this_->LoadFile(path);
return INFO::CONTINUE;
}
int CFormationCollection::LoadTemplates()
{
// Load all files in formations and subdirectories.
THROW_ERR( fs_util::ForEachFile(g_VFS, L"formations/", LoadFormationThunk, (uintptr_t)this, L"*.xml", fs_util::DIR_RECURSIVE));
return 0;
}
CFormation* CFormationCollection::GetTemplate( const CStrW& name )
{
// Check whether this template has already been loaded
templateMap::iterator it = m_templates.find( name );
if( it != m_templates.end() )
return( it->second );
// Find the filename corresponding to this template
templateFilenameMap::iterator filename_it = m_templateFilenames.find( name );
if( filename_it == m_templateFilenames.end() )
return( NULL );
VfsPath pathname( filename_it->second );
//Try to load to the formation
CFormation* newTemplate = new CFormation();
if( !newTemplate->LoadXml( pathname ) )
{
LOG(CLogger::Error, LOG_CATEGORY, L"CFormationCollection::LoadTemplates(): Couldn't load template \"%ls\"", pathname.string().c_str());
delete newTemplate;
return( NULL );
}
LOG(CLogger::Normal, LOG_CATEGORY, L"CFormationCollection::LoadTemplates(): Loaded template \"%ls\"", pathname.string().c_str());
m_templates[name] = newTemplate;
return newTemplate;
}
void CFormationCollection::GetFormationNames( std::vector<CStrW>& names )
{
for( templateFilenameMap::iterator it = m_templateFilenames.begin(); it != m_templateFilenames.end(); ++it )
if( ! (it->first.length() > 8 && it->first.Left(8) == L"template"))
names.push_back( it->first );
}
CFormationCollection::~CFormationCollection()
{
for( templateMap::iterator it = m_templates.begin(); it != m_templates.end(); ++it )
delete( it->second );
}

View File

@ -1,55 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
//FormationCollection.h
//Nearly identical to EntityTemplateCollection and associates i.e. EntityTemplate, Entity.
//This is the manager of the entity formation "templates"
#ifndef INCLUDED_FORMATIONCOLLECTION
#define INCLUDED_FORMATIONCOLLECTION
#include <vector>
#include "lib/file/vfs/vfs_path.h"
#include "ps/CStr.h"
#include "ps/Singleton.h"
#include "ps/Filesystem.h"
#include "Formation.h"
#define g_EntityFormationCollection CFormationCollection::GetSingleton()
class CFormationCollection : public Singleton<CFormationCollection>
{
typedef std::map<CStrW, CFormation*> templateMap;
typedef std::map<CStrW, VfsPath> templateFilenameMap;
templateMap m_templates;
templateFilenameMap m_templateFilenames;
public:
~CFormationCollection();
CFormation* GetTemplate( const CStrW& formationType );
int LoadTemplates();
void LoadFile( const VfsPath& path );
// Create a list of the names of all base entities, excluding template_*,
// for display in ScEd's entity-selection box.
void GetFormationNames( std::vector<CStrW>& names );
};
#endif

View File

@ -1,162 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "FormationManager.h"
#include "FormationCollection.h"
#include "Entity.h"
#include "ps/CStr.h"
#include "Formation.h"
#include "EntityFormation.h"
#include "EventHandlers.h"
#include "ps/Vector2D.h"
CFormationManager::~CFormationManager()
{
for ( size_t i=0; i<m_formations.size(); i++ )
delete m_formations[i];
}
void CFormationManager::CreateFormation( CEntityList& entities, CStrW& name )
{
if ( entities.empty() )
{
debug_warn(L"Attempting to create a formation with no entities");
return;
}
CFormation* base = g_EntityFormationCollection.GetTemplate(name);
if (!base)
return;
if ( entities.size() < (size_t)base->m_required )
return;
CEntityFormation* tmp = new CEntityFormation( base, m_formations.size() );
m_formations.push_back( tmp );
CEntityList accepted = AddUnitList( entities, (int)m_formations.size()-1 );
//Find average position
CVector3D average( 0.0f, 0.0f, 0.0f );
for ( CEntityList::iterator it=accepted.begin(); it != accepted.end(); it++ )
average += (*it)->m_position;
average = average * ( 1.0f / (float)entities.size() );
CVector2D average2D(average.X, average.Z);
m_formations.back()->m_position = average2D;
m_formations.back()->UpdateFormation();
if ( accepted.size() < (size_t)base->m_required )
DestroyFormation( m_formations.size()-1 );
}
void CFormationManager::DestroyFormation( size_t form )
{
if ( form >= m_formations.size())
{
debug_warn(L"CFormationManager::DestroyFormation--invalid entity");
return;
}
FormIterator it=m_formations.begin() + form;
CEntityList entities = (*it)->GetEntityList();
//Notify the script that we've "left" the formation
for ( size_t i=0; i<entities.size(); i++ )
{
CEntity* entity = entities[i];
entity->DispatchFormationEvent( CFormationEvent::FORMATION_LEAVE );
(*it)->RemoveUnit(entity);
}
delete *it;
*it = NULL;
m_formations.erase( it );
UpdateIndexes( form );
}
bool CFormationManager::AddUnit( CEntity* entity, size_t& form )
{
if ( !IsValidFormation(form) )
return false;
if ( entity->m_formation > -1 )
{
if ( !RemoveUnit( entity ) )
--form;
}
FormIterator it = m_formations.begin() + form;
//Adding too many?
if ( (*it)->m_numEntities == (*it)->m_base->m_numSlots )
{
CFormation* next = g_EntityFormationCollection.GetTemplate((*it)->m_base->m_next);
if (next)
(*it)->SwitchBase( next );
}
if ( (*it)->AddUnit(entity) ) //This unit might be better
{
entity->DispatchFormationEvent( CFormationEvent::FORMATION_ENTER );
return true;
}
return false;
}
CEntityList CFormationManager::AddUnitList( CEntityList& entities, size_t form )
{
CEntityList accepted;
for ( CEntityList::iterator it=entities.begin(); it != entities.end(); it++ )
{
CEntity* entity = *it;
if ( AddUnit( entity, form ) )
accepted.push_back( *it );
}
return accepted;
}
bool CFormationManager::RemoveUnit( CEntity* entity )
{
if ( !IsValidFormation(entity->m_formation) )
return true;
FormIterator it = m_formations.begin() + entity->m_formation;
if ( (*it)->m_numEntities == (*it)->m_base->m_required )
{
CFormation* prior = g_EntityFormationCollection.GetTemplate((*it)->m_base->m_prior);
//Disband formation
if (!prior)
{
DestroyFormation( entity->m_formation );
return false;
}
}
entity->DispatchFormationEvent( CFormationEvent::FORMATION_LEAVE );
(*it)->RemoveUnit( entity );
return true;
}
bool CFormationManager::RemoveUnitList( CEntityList& entities )
{
for ( CEntityList::iterator it=entities.begin(); it != entities.end(); it++ )
{
CEntity* entity = *it;
if ( !RemoveUnit(entity) )
return false;
}
return true;
}
CEntityFormation* CFormationManager::GetFormation(size_t form)
{
if ( IsValidFormation(form) )
return m_formations[form];
return NULL;
}
void CFormationManager::UpdateIndexes( size_t update )
{
for ( ; update < m_formations.size(); update++ )
m_formations[update]->ResetIndex( update );
}

View File

@ -1,63 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
//This keeps track of all the formations exisisting in the game.
#ifndef INCLUDED_FORMATIONMANAGER
#define INCLUDED_FORMATIONMANAGER
#include "ps/Singleton.h"
#include "scripting/DOMEvent.h"
#define g_FormationManager CFormationManager::GetSingleton()
class CEntity;
class CStr;
class CFormation;
class CVector2D;
class CEntityFormation;
struct CEntityList;
class CFormationManager : public Singleton<CFormationManager>
{
#define FormIterator std::vector<CEntityFormation*>::iterator
public:
CFormationManager() {}
~CFormationManager();
void CreateFormation( CEntityList& entities, CStrW& name );
//entity is any unit in the formation
void DestroyFormation( size_t form );
inline bool IsValidFormation( size_t index )
{
return (index < m_formations.size());
}
bool AddUnit( CEntity* entity, size_t& form );
CEntityList AddUnitList( CEntityList& entities, size_t form );
//Returns false if the formation is destroyed
bool RemoveUnit( CEntity* entity );
bool RemoveUnitList( CEntityList& entities );
CEntityFormation* GetFormation(size_t form);
void UpdateIndexes( size_t update );
private:
std::vector<CEntityFormation*> m_formations;
};
#endif // INCLUDED_FORMATIONMANAGER

View File

@ -1,271 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "LOSManager.h"
#include "ps/Game.h"
#include "ps/Player.h"
#include "ps/World.h"
#include "graphics/Terrain.h"
#include "Entity.h"
#include "EntityTemplate.h"
#include "graphics/Unit.h"
#include "maths/Bound.h"
#include "graphics/Model.h"
#include "lib/allocators/allocators.h" // matrix_alloc
#include "lib/timer.h"
CLOSManager::CLOSManager() : m_LOSSetting(LOS_SETTING_NORMAL), m_FogOfWar(true)
{
#ifdef _2_los
m_Explored = 0;
m_Visible = 0;
#else
m_VisibilityMatrix = 0;
#endif
m_TilesPerSide = m_TilesPerSide_1 = 0;
}
CLOSManager::~CLOSManager()
{
#ifdef _2_los
matrix_free((void**)m_Explored);
m_Explored = 0;
matrix_free((void**)m_Visible);
m_Visible = 0;
#else
matrix_free((void**)m_VisibilityMatrix);
m_VisibilityMatrix = 0;
#endif
}
void CLOSManager::Initialize(ELOSSetting losSetting, bool fogOfWar)
{
// Set special LOS setting
m_LOSSetting = losSetting;
m_FogOfWar = fogOfWar;
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
m_TilesPerSide = terrain->GetVerticesPerSide() - 1;
m_TilesPerSide_1 = m_TilesPerSide-1;
// Create the LOS data arrays
#ifdef _2_los
m_Explored = (int**)matrix_alloc(m_TilesPerSide, m_TilesPerSide, sizeof(int));
m_Visible = (int**)matrix_alloc(m_TilesPerSide, m_TilesPerSide, sizeof(int));
#else
m_VisibilityMatrix = (u16**)matrix_alloc(m_TilesPerSide, m_TilesPerSide, sizeof(u16));
#endif
// TODO: This memory should be freed somewhere when the engine supports
// multiple sessions without restarting the program.
// JW: currently free it in the dtor
// Set initial values
#ifdef _2_los
int explored_value = (m_LOSSetting == LOS_SETTING_EXPLORED || m_LOSSetting == LOS_SETTING_ALL_VISIBLE)? 0xFF : 0;
int vis_value = (m_LOSSetting == LOS_SETTING_ALL_VISIBLE)? 0xFF : 0;
#else
u16 vis_value = 0;
if(m_LOSSetting == LOS_SETTING_EXPLORED || m_LOSSetting == LOS_SETTING_ALL_VISIBLE)
for(int i = 0; i < 8; i++) vis_value |= LOS_EXPLORED << (i*2);
if(m_LOSSetting == LOS_SETTING_ALL_VISIBLE || (m_LOSSetting == LOS_SETTING_EXPLORED && !m_FogOfWar) )
for(int i = 0; i < 8; i++) vis_value |= LOS_VISIBLE << (i*2);
#endif
for(size_t x=0; x<m_TilesPerSide; x++)
{
#ifdef _2_los
memset(m_Explored[x], explored_value, m_TilesPerSide*sizeof(int));
memset(m_Visible [x], vis_value , m_TilesPerSide*sizeof(int));
#else
for(size_t y=0; y<m_TilesPerSide; y++)
for(size_t x=0; x<m_TilesPerSide; x++)
m_VisibilityMatrix[y][x] = vis_value;
#endif
}
// Just Update() to set the visible array and also mark currently visible tiles as explored.
// NOTE: this will have to be changed if we decide to use incremental LOS
Update();
}
// NOTE: this will have to be changed if we decide to use incremental LOS
void CLOSManager::Update()
{
if(m_LOSSetting == LOS_SETTING_ALL_VISIBLE)
return;
// Clear the visible array
#ifdef _2_los
if( m_FogOfWar )
{
for(int x=0; x<m_TilesPerSide; x++)
{
memset(m_Visible[x], 0, m_TilesPerSide*sizeof(int));
}
}
#else
if( m_FogOfWar )
{
u16 not_all_vis = 0xFFFF;
for(int i = 0; i < 8; i++)
not_all_vis &= ~(LOS_VISIBLE << (i*2));
for(size_t y=0; y<m_TilesPerSide; y++)
for(size_t x=0; x<m_TilesPerSide; x++)
m_VisibilityMatrix[y][x] &= not_all_vis;
}
#endif
// Set visibility for each entity
std::vector<CEntity*> extant;
// g_EntityManager.GetExtant(extant);
for(size_t i=0; i<extant.size(); i++)
{
CEntity* e = extant[i];
ssize_t los = e->m_los;
if(los == 0)
continue;
#ifdef _2_los
size_t mask = (size_t(1) << e->GetPlayer()->GetPlayerID());
#else
size_t shift = e->GetPlayer()->GetPlayerID()*2;
#endif
ssize_t cx, cz;
CTerrain::CalcFromPosition(e->m_position.X, e->m_position.Z, cx, cz);
ssize_t minX = std::max(cx-los, ssize_t(0));
ssize_t minZ = std::max(cz-los, ssize_t(0));
ssize_t maxX = std::min(cx+los, ssize_t(m_TilesPerSide_1));
ssize_t maxZ = std::min(cz+los, ssize_t(m_TilesPerSide_1));
for(ssize_t x=minX; x<=maxX; x++)
{
for(ssize_t z=minZ; z<=maxZ; z++)
{
if((x-cx)*(x-cx) + (z-cz)*(z-cz) <= los*los)
{
#ifdef _2_los
m_Visible[x][z] |= mask;
m_Explored[x][z] |= mask;
#else
m_VisibilityMatrix[x][z] |= (LOS_EXPLORED|LOS_VISIBLE) << shift;
#endif
}
}
}
}
}
size_t LOS_GetTokenFor(size_t player_id)
{
#ifdef _2_los
return size_t(1) << player_id;
#else
return player_id*2;
#endif
}
//TIMER_ADD_CLIENT(tc_getstatus);
ELOSStatus CLOSManager::GetStatus(ssize_t tx, ssize_t tz, CPlayer* player)
{
//TIMER_ACCRUE(tc_getstatus);
// Ensure that units off the map don't cause the visibility arrays to be
// accessed out of bounds
if ((size_t)tx >= m_TilesPerSide || (size_t)tz >= m_TilesPerSide)
return LOS_VISIBLE; // because we don't want them to be permanently hidden
// TODO: Make the mask depend on the player's diplomacy (just OR all his allies' masks)
#ifdef _2_los
const int mask = player->GetLOSToken();
if((m_Visible[tx][tz] & mask) || m_LOSSetting == LOS_SETTING_ALL_VISIBLE)
{
return LOS_VISIBLE;
}
else if((m_Explored[tx][tz] & mask) || m_LOSSetting == LOS_SETTING_EXPLORED)
{
return LOS_EXPLORED;
}
else
{
return LOS_UNEXPLORED;
}
#else
const size_t shift = player->GetLOSToken();
return (ELOSStatus)((m_VisibilityMatrix[tx][tz] >> shift) & 3);
#endif
}
ELOSStatus CLOSManager::GetStatus(float fx, float fz, CPlayer* player)
{
ssize_t ix, iz;
CTerrain::CalcFromPosition(fx, fz, ix, iz);
return GetStatus(ix, iz, player);
}
EUnitLOSStatus CLOSManager::GetUnitStatus(CUnit* unit, CPlayer* player)
{
CVector3D centre;
// For entities, we must use the simulation position so that we stay synchronised
// (because the output of this function will presumably affect AI)
CEntity* entity = unit->GetEntity();
if (entity)
centre = entity->m_position;
else
centre = unit->GetModel().GetTransform().GetTranslation();
ELOSStatus status = GetStatus(centre.X, centre.Z, player);
if(status & LOS_VISIBLE)
return UNIT_VISIBLE;
if(status & LOS_EXPLORED)
{
if(!entity || entity->m_base->m_visionPermanent)
{
// both actors (which are usually for decoration) and units with the
// permanent flag should be remembered
return UNIT_REMEMBERED;
// TODO: the unit status system will have to be replaced with a "ghost actor"
// system so that we can't remember units that we haven't seen and so we can
// see permanent units that have died but that we haven't been near lately
}
}
return UNIT_HIDDEN;
}
EUnitLOSStatus CLOSManager::GetUnitStatus(CEntity* entity, CPlayer* player)
{
return GetUnitStatus( entity->m_actor, player );
}

View File

@ -1,104 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// LOSManager.h
//
// Maintains and updates line of sight data (including Shroud of Darkness
// and Fog of War).
//
// Usage:
// - Initialize() is called when the game is started to allocate the visibility arrays
// - Update() is called each frame by CSimulation::Update() to update the visibility arrays
// - m_MapRevealed can be set to true to reveal the entire map (remove both LOS and FOW)
// - GetStatus can be used to obtain the LOS status of a tile or a world-space point
// - GetUnitStatus returns the LOS status of an entity or actor
#ifndef INCLUDED_LOSMANAGER
#define INCLUDED_LOSMANAGER
class CUnit;
class CEntity;
class CPlayer;
#undef _2_los
enum ELOSStatus
{
LOS_VISIBLE = 2, // tile is currently in LOS of one of the player's units
LOS_EXPLORED = 1, // tile was explored before but is now in Fog of War
LOS_UNEXPLORED = 0 // tile is unexplored and therefore in Shroud of Darkness
};
// if changing these values, adapt ScriptGlue!RevealMap
enum ELOSSetting
{
LOS_SETTING_NORMAL,
LOS_SETTING_EXPLORED,
LOS_SETTING_ALL_VISIBLE
};
enum EUnitLOSStatus
{
UNIT_VISIBLE = 2, // unit is in LOS of one of the player's units
UNIT_REMEMBERED = 1, // unit was seen before and is permanent but is no longer in LOS
UNIT_HIDDEN = 0 // unit is either not permanent or was never seen before
};
extern size_t LOS_GetTokenFor(size_t player_id);
class CLOSManager
{
#ifdef _2_los
int** m_Explored; // (m_Explored[x][z] & (1<<p) says whether player p has explored tile (x,z),
// i.e. has removed Shroud of Darkness from it.
int** m_Visible; // (m_Visible[x][z] & (1<<p)) says whether player p currently sees tile (x,z).
// NOTE: This will have to be changed to a 3D array where each element stores the number of units
// of a certain player that can see a certain tile if we want to use incremental LOS.
#else
u16** m_VisibilityMatrix;
#endif
size_t m_TilesPerSide;
size_t m_TilesPerSide_1; // as above, -1
public:
ELOSSetting m_LOSSetting;
bool m_FogOfWar;
CLOSManager();
~CLOSManager();
void Initialize(ELOSSetting losSetting, bool fogOfWar);
void Update();
// Get LOS status for a tile (in tile coordinates)
ELOSStatus GetStatus(ssize_t tx, ssize_t tz, CPlayer* player);
// Get LOS status for a point (in game coordinates)
ELOSStatus GetStatus(float fx, float fz, CPlayer* player);
// Returns whether a given actor is visible to the given player
EUnitLOSStatus GetUnitStatus(CUnit* unit, CPlayer* player);
// Returns whether a given entity is visible to the given player
EUnitLOSStatus GetUnitStatus(CEntity* entity, CPlayer* player);
};
#endif

View File

@ -1,249 +0,0 @@
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include <vector>
#include "graphics/Model.h"
#include "graphics/Terrain.h"
#include "graphics/Unit.h"
#include "graphics/UnitManager.h"
#include "maths/MathUtil.h"
#include "network/NetMessage.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#include "ps/GameAttributes.h"
#include "ps/Loader.h"
#include "ps/LoaderThunks.h"
#include "ps/Profile.h"
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
#include "simulation/Entity.h"
#include "simulation/EntityFormation.h"
#include "simulation/EntityManager.h"
#include "simulation/EntityTemplateCollection.h"
#include "simulation/LOSManager.h"
#include "simulation/Scheduler.h"
#include "simulation/Simulation.h"
#include "simulation/TerritoryManager.h"
#include "simulation/TriggerManager.h"
#define LOG_CATEGORY L"simulation"
CSimulation::CSimulation(CGame *pGame):
m_pGame(pGame),
m_pWorld(pGame->GetWorld()),
m_DeltaTime(0),
m_Time(0)
{
}
CSimulation::~CSimulation()
{
}
int CSimulation::Initialize(CGameAttributes* pAttribs)
{
m_Random.seed(0); // TODO: Store a random seed in CGameAttributes and synchronize it accross the network
// Call the game startup script
// TODO: Maybe don't do this if we're in Atlas
// [2006-06-26 20ms]
g_ScriptingHost.RunScript( L"scripts/game_startup.js" );
// [2006-06-26 3647ms]
// g_EntityManager.m_screenshotMode = pAttribs->m_ScreenshotMode;
// g_EntityManager.InitializeAll();
// [2006-06-26: 61ms]
m_pWorld->GetLOSManager()->Initialize((ELOSSetting)pAttribs->m_LOSSetting, pAttribs->m_FogOfWar);
m_pWorld->GetTerritoryManager()->Initialize();
return 0;
}
void CSimulation::RegisterInit(CGameAttributes *pAttribs)
{
RegMemFun1(this, &CSimulation::Initialize, pAttribs, L"CSimulation", 3900);
}
bool CSimulation::Update(double UNUSED(frameTime))
{
bool ok = true;
return ok;
}
void CSimulation::DiscardMissedUpdates()
{
if (m_DeltaTime > 0.0)
m_DeltaTime = 0.0;
}
void CSimulation::Interpolate(double UNUSED(frameTime))
{
}
void CSimulation::Interpolate(double frameTime, double offset)
{
PROFILE( "simulation interpolation" );
const std::vector<CUnit*>& units = m_pWorld->GetUnitManager().GetUnits();
for (size_t i = 0; i < units.size(); ++i)
units[i]->UpdateModel((float)frameTime);
// g_EntityManager.InterpolateAll(offset);
}
void CSimulation::Simulate()
{
}
// Location randomizer, for group orders...
// Having the group turn up at the destination with /some/ sort of cohesion is
// good but tasking them all to the exact same point will leave them brawling
// for it at the other end (it shouldn't, but the PASAP pathfinder is too
// simplistic)
// Task them all to a point within a radius of the target, radius depends upon
// the number of units in the group.
void RandomizeLocations(const CEntityOrder& order, const std::vector<HEntity> &entities, bool isQueued)
{
float radius = 2.0f * sqrt( (float)entities.size() - 1 );
CSimulation* sim = g_Game->GetSimulation();
for (std::vector<HEntity>::const_iterator it = entities.begin(); it < entities.end(); it++)
{
if(!*it)
continue;
float _x, _y;
CEntityOrder randomizedOrder = order;
do
{
_x = sim->RandFloat() * 2.0f - 1.0f;
_y = sim->RandFloat() * 2.0f - 1.0f;
}
while( ( _x * _x ) + ( _y * _y ) > 1.0f );
randomizedOrder.m_target_location.x += _x * radius;
randomizedOrder.m_target_location.y += _y * radius;
// Clamp it to within the map, just in case.
float mapsize = (float)g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide() * CELL_SIZE;
randomizedOrder.m_target_location.x = clamp(randomizedOrder.m_target_location.x, 0.0f, mapsize);
randomizedOrder.m_target_location.y = clamp(randomizedOrder.m_target_location.y, 0.0f, mapsize);
if( !isQueued )
(*it)->ClearOrders();
(*it)->PushOrder( randomizedOrder );
}
}
void FormationLocations(const CEntityOrder& order, const std::vector<HEntity> &entities, bool isQueued)
{
const CVector2D upvec(0.0f, 1.0f);
const CEntityFormation* formation = entities.front()->GetFormation();
for (std::vector<HEntity>::const_iterator it = entities.begin(); it != entities.end(); it++)
{
if(!*it)
continue;
CEntityOrder orderCopy = order;
CVector2D posDelta = orderCopy.m_target_location - formation->GetPosition();
CVector2D formDelta = formation->GetSlotPosition( (*it)->m_formationSlot );
posDelta = posDelta.Normalize();
//Rotate the slot position's offset vector according to the rotation of posDelta.
CVector2D rotDelta;
float deltaCos = posDelta.Dot(upvec);
float deltaSin = sinf( acosf(deltaCos) );
rotDelta.x = formDelta.x * deltaCos - formDelta.y * deltaSin;
rotDelta.y = formDelta.x * deltaSin + formDelta.y * deltaCos;
orderCopy.m_target_location += rotDelta;
// Clamp it to within the map, just in case.
float mapsize = (float)g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide() * CELL_SIZE;
orderCopy.m_target_location.x = clamp(orderCopy.m_target_location.x, 0.0f, mapsize);
orderCopy.m_target_location.y = clamp(orderCopy.m_target_location.y, 0.0f, mapsize);
if( !isQueued )
(*it)->ClearOrders();
(*it)->PushOrder( orderCopy );
}
}
void QueueOrder(const CEntityOrder& order, const std::vector<HEntity> &entities, bool isQueued)
{
for (std::vector<HEntity>::const_iterator it = entities.begin(); it < entities.end(); it++)
{
if (!*it)
continue;
if( !isQueued )
(*it)->ClearOrders();
(*it)->PushOrder( order );
}
}
size_t CSimulation::TranslateMessage(CNetMessage* pMsg, size_t clientMask, void* UNUSED(userdata))
{
return clientMask;
}
size_t CSimulation::GetMessageMask(CNetMessage* UNUSED(pMsg), size_t UNUSED(oldMask), void* UNUSED(userdata))
{
//CSimulation *pSimulation=(CSimulation *)userdata;
// Pending a complete visibility/minimal-update implementation, we'll
// simply select the first 32 connected clients ;-)
return 0xFFFFFFFF;
}
void CSimulation::QueueLocalCommand(CNetMessage *pMsg)
{
}
// Get a random integer between 0 and maxVal-1 from the simulation's random number generator
int CSimulation::RandInt(int maxVal)
{
boost::uniform_smallint<int> distr(0, maxVal-1);
return distr(m_Random);
}
// Get a random float in [0, 1) from the simulation's random number generator
float CSimulation::RandFloat()
{
// Cannot use uniform_01 here because it is not a real distribution, but rather an
// utility class that makes a copy of the generator, and therefore it would repeatedly
// return the same values because it never modifies our copy of the generator.
boost::uniform_real<float> distr(0.0f, 1.0f);
return distr(m_Random);
}

View File

@ -1,87 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_SIMULATION
#define INCLUDED_SIMULATION
#include <boost/random.hpp>
class CGame;
class CGameAttributes;
class CWorld;
class CNetMessage;
class CSimulation
{
CGame *m_pGame;
CWorld *m_pWorld;
// Current game time; store as double for precision
double m_Time;
double m_DeltaTime;
// Random number generator
boost::mt19937 m_Random;
// Simulate: move the deterministic simulation forward by one interval
void Simulate();
// Interpolate: interpolate a data point for rendering between simulation
// frames.
void Interpolate(double frameTime, double offset);
public:
CSimulation(CGame *pGame);
~CSimulation();
void RegisterInit(CGameAttributes *pGameAttributes);
int Initialize(CGameAttributes *pGameAttributes);
// Perform simulation updates for the specified elapsed time. If it is
// shorter than the time until the next simulation turn, nothing happens.
// Returns false if it can't keep up with the desired simulation rate.
bool Update(double frameTime);
// If the last Update couldn't keep up with the desired rate, ignore that
// and don't try to catch up when Update is called again. Will completely break
// synchronisation of sim time vs real time.
void DiscardMissedUpdates();
// Update the graphical representations of the simulation by the given time.
void Interpolate(double frameTime);
// Calculate the message mask of a message to be queued
static size_t GetMessageMask(CNetMessage *, size_t oldMask, void *userdata);
// Translate the command message into an entity order and queue it
// Returns oldMask
static size_t TranslateMessage(CNetMessage *, size_t oldMask, void *userdata);
void QueueLocalCommand(CNetMessage *pMsg);
// Get a random integer between 0 and maxVal-1 from the simulation's random number generator
int RandInt(int maxVal);
// Get a random float in [0, 1) from the simulation's random number generator
float RandFloat();
// Get game time
inline double GetTime() { return m_Time; }
};
#endif

View File

@ -1,509 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include <boost/unordered_set.hpp>
#include "Technology.h"
#include "TechnologyCollection.h"
#include "ps/CStr.h"
#include "ps/CLogger.h"
#include "scripting/ScriptingHost.h"
#include "ps/XML/Xeromyces.h"
#include "ps/XML/XeroXMB.h"
#include "Entity.h"
#include "EntityTemplate.h"
#include "EntityTemplateCollection.h"
#include "ps/Player.h"
#include "scripting/ScriptableComplex.inl"
#define LOG_CATEGORY L"Techs"
boost::unordered_set<CStr> CTechnology::m_scriptsLoaded;
CTechnology::CTechnology( const CStrW& name, CPlayer* player )
: m_Name(name), m_player(player)
{
m_researched = false;
m_excluded = false;
m_inProgress = false;
}
bool CTechnology::LoadXml( const VfsPath& pathname )
{
CXeromyces XeroFile;
if (XeroFile.Load(pathname) != PSRETURN_OK)
return false;
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
EL(tech);
EL(id);
EL(req);
EL(effect);
#undef EL
XMBElement Root = XeroFile.GetRoot();
if ( Root.GetNodeName() != el_tech )
{
LOG(CLogger::Error, LOG_CATEGORY, L"CTechnology: XML root was not \"Tech\" in file %ls. Load failed.", pathname.string().c_str() );
return false;
}
XMBElementList RootChildren = Root.GetChildNodes();
bool ret;
for ( int i=0; i<RootChildren.Count; ++i )
{
XMBElement element = RootChildren.Item(i);
int name = element.GetNodeName();
if ( name == el_id )
ret = LoadElId( element, XeroFile );
else if ( name == el_req )
ret = LoadElReq( element, XeroFile );
else if ( name == el_effect )
ret = LoadElEffect( element, XeroFile, pathname );
else
continue;
if ( !ret )
{
LOG(CLogger::Error, LOG_CATEGORY, L"CTechnology: Load failed for file %ls", pathname.string().c_str() );
return false;
}
}
return true;
}
bool CTechnology::LoadElId( XMBElement ID, CXeromyces& XeroFile )
{
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
EL(generic);
EL(specific);
EL(icon);
EL(icon_cell);
EL(classes);
EL(rollover);
EL(history);
#undef EL
XMBElementList children = ID.GetChildNodes();
for ( int i=0; i<children.Count; ++i )
{
XMBElement element = children.Item(i);
int name = element.GetNodeName();
CStr8 value = element.GetText();
if ( name == el_generic )
m_Generic = value;
else if ( name == el_specific )
m_Specific = value;
else if ( name == el_icon )
m_Icon = value;
else if ( name == el_icon_cell )
m_IconCell = value.ToInt();
else if ( name == el_classes )
m_Classes = value;
else if ( name == el_rollover )
continue;
else if ( name == el_history )
m_History = value;
else
{
const CStrW tagName(XeroFile.GetElementString(name));
LOG(CLogger::Error, LOG_CATEGORY, L"CTechnology: invalid tag %ls for XML file", tagName.c_str() );
return false;
}
}
return true;
}
bool CTechnology::LoadElReq( XMBElement Req, CXeromyces& XeroFile )
{
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
EL(time);
EL(resource);
EL(tech);
EL(entity);
#undef EL
XMBElementList children = Req.GetChildNodes();
for ( int i=0; i<children.Count; ++i )
{
XMBElement element = children.Item(i);
int name = element.GetNodeName();
CStr8 value = element.GetText();
if ( name == el_time )
m_ReqTime = value.ToFloat();
else if ( name == el_resource )
{
XMBElementList resChildren = element.GetChildNodes();
for ( int j=0; j<resChildren.Count; ++j )
{
XMBElement resElement = resChildren.Item(j);
CStr8 resName = XeroFile.GetElementString( resElement.GetNodeName() );
CStr8 resValue = resElement.GetText();
// Add each resource as a property using its name in the XML file
AddProperty( CStrW(resName).LowerCase(), resValue);
}
}
else if ( name == el_entity )
{
m_ReqEntities.push_back( value );
}
else if ( name == el_tech )
{
m_ReqTechs.push_back( value );
}
else
{
const CStrW tagName(XeroFile.GetElementString(name));
LOG(CLogger::Error, LOG_CATEGORY, L"CTechnology: invalid tag %ls for XML file", tagName.c_str() );
return false;
}
}
return true;
}
bool CTechnology::LoadElEffect( XMBElement effect, CXeromyces& XeroFile, const VfsPath& pathname )
{
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
EL(target);
EL(pair);
EL(modifier);
EL(attribute);
EL(value);
EL(set);
EL(script);
EL(function);
AT(name);
AT(file);
#undef EL
#undef AT
XMBElementList children = effect.GetChildNodes();
for ( int i=0; i<children.Count; ++i )
{
XMBElement element = children.Item(i);
int name = element.GetNodeName();
CStr value = element.GetText();
if ( name == el_target )
m_Targets.push_back(value);
else if ( name == el_pair )
m_Pairs.push_back(value);
else if ( name == el_modifier )
{
XMBElementList modChildren = element.GetChildNodes();
m_Modifiers.push_back(Modifier());
for ( int j=0; j<modChildren.Count; ++j )
{
XMBElement modElement = modChildren.Item(j);
CStrW modValue = modElement.GetText();
if ( modElement.GetNodeName() == el_attribute)
m_Modifiers.back().attribute = modValue;
else if ( modElement.GetNodeName() == el_value )
{
if( modValue.size() == 0)
{
LOG(CLogger::Error, LOG_CATEGORY, L"CTechnology::LoadXml(%ls): invalid Modifier value (empty string)", pathname.string().c_str() );
m_Modifiers.pop_back();
return false;
}
if( modValue[modValue.size()-1] == '%' )
{
m_Modifiers.back().isPercent = true;
modValue = modValue.substr( 0, modValue.size()-1 );
}
m_Modifiers.back().value = modValue.ToFloat();
}
else
{
LOG(CLogger::Error, LOG_CATEGORY, L"CTechnology::LoadXml(%ls): invalid tag inside \"Modifier\" tag", pathname.string().c_str() );
m_Modifiers.pop_back();
return false;
}
}
}
else if ( name == el_set )
{
XMBElementList setChildren = element.GetChildNodes();
m_Sets.push_back(Modifier());
for ( int j=0; j<setChildren.Count; ++j )
{
XMBElement setElement = setChildren.Item(j);
CStrW setValue = setElement.GetText();
if ( setElement.GetNodeName() == el_attribute)
m_Sets.back().attribute = setValue;
else if ( setElement.GetNodeName() == el_value )
m_Sets.back().value = setValue.ToFloat();
else
{
LOG(CLogger::Error, LOG_CATEGORY, L"CTechnology::LoadXml(%ls): invalid tag inside \"Set\" tag", pathname.string().c_str() );
m_Sets.pop_back();
return false;
}
}
}
else if ( name == el_script )
{
CStr Include = element.GetAttributes().GetNamedItem( at_file );
if( !Include.empty() && m_scriptsLoaded.find( Include ) == m_scriptsLoaded.end() )
{
m_scriptsLoaded.insert( Include );
g_ScriptingHost.RunScript( CStrW(Include) );
}
CStr Inline = element.GetText();
if( !Inline.empty() )
{
CStr pathname_c = CStr(pathname.string());
g_ScriptingHost.RunMemScript( Inline.c_str(), Inline.length(), pathname_c.c_str(), element.GetLineNumber() );
}
}
else if ( name == el_function )
{
utf16string funcName = element.GetAttributes().GetNamedItem( at_name );
CStr Inline = element.GetText();
if ( funcName != utf16string() )
{
jsval fnval;
JSBool ret = JS_GetUCProperty( g_ScriptingHost.GetContext(), g_ScriptingHost.GetGlobalObject(), funcName.c_str(), funcName.size(), &fnval );
debug_assert( ret );
JSFunction* fn = JS_ValueToFunction( g_ScriptingHost.GetContext(), fnval );
if( !fn )
{
LOG(CLogger::Error, LOG_CATEGORY, L"CTechnology::LoadXml: Function does not exist for %ls in file %ls. Load failed.", CStrW(funcName).c_str(), pathname.string().c_str() );
return false;
}
m_effectFunction.SetFunction( fn );
}
else if ( Inline != CStr() )
m_effectFunction.Compile( pathname.string() + L"::" + CStrW(funcName) + L" (" + CStrW( element.GetLineNumber() ) + L")", Inline );
//(No error needed; scripts are optional)
}
else
{
const CStrW tagName(XeroFile.GetElementString(name));
LOG(CLogger::Error, LOG_CATEGORY, L"CTechnology: invalid tag %ls for XML file", tagName.c_str() );
return false;
}
}
return true;
}
bool CTechnology::IsTechValid()
{
if ( m_excluded || m_inProgress )
return false;
for( size_t i=0; i<m_Pairs.size(); i++ )
{
if( g_TechnologyCollection.GetTechnology( m_Pairs[i], m_player )->m_inProgress )
return false;
}
return ( HasReqEntities() && HasReqTechs() );
}
bool CTechnology::HasReqEntities()
{
// Check whether we have ALL the required entities.
std::vector<HEntity> entities;
// m_player->GetControlledEntities(entities);
for ( std::vector<CStr>::iterator it=m_ReqEntities.begin(); it != m_ReqEntities.end(); it++ )
{
// For each required class, check that we have it
bool got = false;
for( CEntityList::iterator it2=entities.begin(); it2 != entities.end(); it2++ )
{
if ( (*it2)->m_classes.IsMember(*it) )
{
got = true;
break;
}
}
if( !got )
return false;
}
return true;
}
bool CTechnology::HasReqTechs()
{
// Check whether we have ANY of the required techs (this is slightly confusing but required for
// the way the tech system is currently planned; ideally we'd have an <Or> or <And> in the XML).
if ( m_ReqTechs.size() == 0 )
return true;
for ( std::vector<CStr>::iterator it=m_ReqTechs.begin(); it != m_ReqTechs.end(); it++ )
{
if ( g_TechnologyCollection.GetTechnology( (CStrW)*it, m_player )->IsResearched() )
{
return true;
}
}
return false;
}
void CTechnology::Apply( CEntity* entity )
{
// Find out if the unit has one of our target classes
bool ok = false;
for ( std::vector<CStr>::iterator it = m_Targets.begin(); it != m_Targets.end(); it++ )
{
if ( entity->m_classes.IsMember( *it ) )
{
ok = true;
break;
}
}
if( !ok ) return;
// Apply modifiers
for ( std::vector<Modifier>::iterator mod=m_Modifiers.begin(); mod!=m_Modifiers.end(); mod++ )
{
jsval oldVal;
if( entity->GetProperty( g_ScriptingHost.getContext(), mod->attribute, &oldVal ) )
{
float modValue;
if( mod->isPercent )
{
jsval baseVal;
entity->m_base->m_unmodified->GetProperty( g_ScriptingHost.getContext(), mod->attribute, &baseVal );
modValue = ToPrimitive<float>(baseVal) * mod->value / 100.0f;
}
else
{
modValue = mod->value;
}
jsval newVal = ToJSVal( ToPrimitive<float>(oldVal) + modValue );
entity->SetProperty( g_ScriptingHost.GetContext(), mod->attribute, &newVal );
}
}
// Apply sets
for ( std::vector<Modifier>::iterator mod=m_Sets.begin(); mod!=m_Sets.end(); mod++ )
{
jsval newVal = ToJSVal( mod->value );
entity->SetProperty( g_ScriptingHost.GetContext(), mod->attribute, &newVal );
}
}
//JS stuff
void CTechnology::ScriptingInit()
{
AddClassProperty(L"name", &CTechnology::m_Name, true);
AddClassProperty(L"player", &CTechnology::m_player, true);
AddClassProperty(L"generic", &CTechnology::m_Generic, true);
AddClassProperty(L"specific", &CTechnology::m_Specific, true);
AddClassProperty(L"icon", &CTechnology::m_Icon); //GUI might want to change this...?
AddClassProperty(L"icon_cell", &CTechnology::m_IconCell);
AddClassProperty(L"classes", &CTechnology::m_Classes, true);
AddClassProperty(L"history", &CTechnology::m_History, true);
AddClassProperty(L"time", &CTechnology::m_ReqTime); //Techs may upgrade research time and cost of other techs
AddClassProperty(L"in_progress", &CTechnology::m_inProgress);
AddMethod<bool, &CTechnology::ApplyEffects>( "applyEffects", 2 );
AddMethod<bool, &CTechnology::IsExcluded>( "isExcluded", 0 );
AddMethod<bool, &CTechnology::IsValid>( "isValid", 0 );
AddMethod<bool, &CTechnology::IsResearched>( "isResearched", 0 );
AddMethod<size_t, &CTechnology::GetPlayerID>( "getPlayerID", 0 );
CJSComplex<CTechnology>::ScriptingInit("Technology");
}
bool CTechnology::ApplyEffects( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
// Unmark ourselves as in progress
m_inProgress = false;
if ( !IsTechValid() )
{
return false;
}
// Disable any paired techs
for ( std::vector<CStr>::iterator it=m_Pairs.begin(); it != m_Pairs.end(); it++ )
g_TechnologyCollection.GetTechnology(*it, m_player)->SetExclusion(true);
// Disable ourselves so we can't be researched twice
m_excluded = true;
// Mark ourselves as researched
m_researched = true;
// Apply effects to all entities
std::vector<HEntity> entities;
// m_player->GetControlledEntities(entities);
for ( size_t i=0; i<entities.size(); ++i )
{
Apply( entities[i] );
}
// Run one-time tech script
if( m_effectFunction )
{
jsval rval;
jsval arg = ToJSVal( m_player );
m_effectFunction.Run( this->GetScript(), &rval, 1, &arg );
}
// Add ourselves to player's researched techs
m_player->AddActiveTech( this );
return true;
}
bool CTechnology::IsValid( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
return IsTechValid();
}
bool CTechnology::IsExcluded( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
return m_excluded;
}
bool CTechnology::IsResearched( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
return IsResearched();
}
size_t CTechnology::GetPlayerID( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
{
return m_player->GetPlayerID();
}

View File

@ -1,110 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// Holds effects of a technology (research item), as well as its status
// (unavailable, researched, in progress, etc).
//
// There is a separate CTechnology object for each tech for each player,
// because the status can be different for different players.
#ifndef INCLUDED_TECHNOLOGY
#define INCLUDED_TECHNOLOGY
#include <vector>
#include "scripting/ScriptableComplex.h"
#include "simulation/ScriptObject.h"
#include "ps/Game.h"
class XMBElement;
class CXeromyces;
class CEntity;
class CTechnology : public CJSComplex<CTechnology>
{
NONCOPYABLE(CTechnology);
friend class CTechnologyCollection;
struct Modifier
{
CStr attribute;
float value;
bool isPercent;
Modifier(): value(0), isPercent(false) {}
};
static boost::unordered_set<CStr> m_scriptsLoaded;
public:
CTechnology(const CStrW& name, CPlayer* player);
~CTechnology() {}
//JS functions
static void ScriptingInit();
bool ApplyEffects( JSContext* cx, uintN argc, jsval* argv );
bool IsValid( JSContext* cx, uintN argc, jsval* argv );
bool IsResearched( JSContext* cx, uintN argc, jsval* argv );
bool IsExcluded( JSContext* cx, uintN argc, jsval* argv );
size_t GetPlayerID( JSContext* cx, uintN argc, jsval* argv );
void Apply( CEntity* entity );
bool IsTechValid();
inline bool IsResearched() { return m_researched; }
void SetExclusion( bool exclude ) { m_excluded=exclude; }
bool LoadXml( const VfsPath& filename );
bool LoadElId( XMBElement ID, CXeromyces& XeroFile );
bool LoadElReq( XMBElement Req, CXeromyces& XeroFile );
bool LoadElEffect( XMBElement Effect, CXeromyces& XeroFile, const VfsPath& pathname);
private:
CStrW m_Name; // name of the tech file
CStrW m_Generic;
CStrW m_Specific;
CStrW m_Icon;
int m_IconCell;
CStrW m_Classes;
CStrW m_History;
float m_ReqTime;
std::vector<CStr> m_ReqEntities;
std::vector<CStr> m_ReqTechs;
std::vector<CStr> m_Pairs;
std::vector<CStr> m_Targets;
std::vector<Modifier> m_Modifiers;
std::vector<Modifier> m_Sets;
CPlayer* m_player; //Which player this tech object belongs to
CScriptObject m_effectFunction;
bool m_excluded;
bool m_researched;
bool m_inProgress;
bool HasReqEntities();
bool HasReqTechs();
// Hack: shouldn't be part of CJSComplex
void RebuildClassSet() {};
};
#endif

View File

@ -1,85 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "TechnologyCollection.h"
#include "ps/CLogger.h"
#include "ps/Player.h"
#define LOG_CATEGORY L"tech"
void CTechnologyCollection::LoadFile( const VfsPath& pathname )
{
const CStrW basename(fs::basename(pathname));
m_techFilenames[basename] = pathname;
}
static LibError LoadTechThunk( const VfsPath& pathname, const FileInfo& UNUSED(fileInfo), uintptr_t cbData )
{
CTechnologyCollection* this_ = (CTechnologyCollection*)cbData;
this_->LoadFile(pathname);
return INFO::CONTINUE;
}
int CTechnologyCollection::LoadTechnologies()
{
// Load all files in techs/ and subdirectories.
THROW_ERR( fs_util::ForEachFile(g_VFS, L"technologies/", LoadTechThunk, (uintptr_t)this, L"*.xml", fs_util::DIR_RECURSIVE));
return 0;
}
CTechnology* CTechnologyCollection::GetTechnology( const CStrW& name, CPlayer* player )
{
// Find player ID
debug_assert( player != 0 );
const size_t id = player->GetPlayerID();
// Check whether this template has already been loaded.
//If previously loaded, all slots will be found, so any entry works.
TechMap::iterator it = m_techs[id].find( name );
if( it != m_techs[id].end() )
return( it->second );
// Find the filename corresponding to this template
TechFilenameMap::iterator filename_it = m_techFilenames.find( name );
if( filename_it == m_techFilenames.end() )
return( NULL );
VfsPath path( filename_it->second );
//Try to load to the tech
CTechnology* newTemplate = new CTechnology( name, player );
if( !newTemplate->LoadXml( path ) )
{
LOG(CLogger::Error, LOG_CATEGORY, L"CTechnologyCollection::GetTechnology(): Couldn't load tech \"%ls\"", path.string().c_str());
delete newTemplate;
return( NULL );
}
m_techs[id][name] = newTemplate;
LOG(CLogger::Normal, LOG_CATEGORY, L"CTechnologyCollection::GetTechnology(): Loaded tech \"%ls\"", path.string().c_str());
return newTemplate;
}
CTechnologyCollection::~CTechnologyCollection()
{
for( size_t id=0; id<PS_MAX_PLAYERS+1; id++ )
for( TechMap::iterator it = m_techs[id].begin(); it != m_techs[id].end(); ++it )
delete( it->second );
}

View File

@ -1,52 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
//Manages the tech templates. More detail: see CFormation and CEntityTemplate (collections)
#ifndef INCLUDED_TECHNOLOGYCOLLECTION
#define INCLUDED_TECHNOLOGYCOLLECTION
#include "ps/CStr.h"
#include "ps/Singleton.h"
#include "Technology.h"
#include "ps/Game.h"
#include "ps/Filesystem.h"
#define g_TechnologyCollection CTechnologyCollection::GetSingleton()
class CTechnologyCollection : public Singleton<CTechnologyCollection>
{
typedef std::map<CStrW, CTechnology*> TechMap;
typedef std::map<CStrW, VfsPath> TechFilenameMap;
TechMap m_techs[PS_MAX_PLAYERS+1];
TechFilenameMap m_techFilenames;
public:
std::vector<CTechnology*> activeTechs[PS_MAX_PLAYERS+1];
CTechnology* GetTechnology( const CStrW& techType, CPlayer* player );
~CTechnologyCollection();
int LoadTechnologies();
// called by non-member trampoline via LoadTechnologies
void LoadFile( const VfsPath& path );
};
#endif

View File

@ -1,368 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "TerritoryManager.h"
#include "graphics/Frustum.h"
#include "graphics/Camera.h"
#include "graphics/GameView.h"
#include "graphics/Model.h"
#include "graphics/Terrain.h"
#include "graphics/Unit.h"
#include "lib/allocators/allocators.h" // matrix_alloc
#include "lib/ogl.h"
#include "lib/timer.h"
#include "maths/Bound.h"
#include "maths/MathUtil.h"
#include "ps/Game.h"
#include "ps/Player.h"
#include "ps/Profile.h"
#include "simulation/Entity.h"
#include "simulation/EntityManager.h"
#include "simulation/EntityManager.h"
#include "simulation/EntityTemplate.h"
#include "simulation/LOSManager.h"
CTerritoryManager::CTerritoryManager()
{
m_TerritoryMatrix = 0;
m_DelayedRecalculate = false;
}
CTerritoryManager::~CTerritoryManager()
{
if(m_TerritoryMatrix)
{
matrix_free( (void**) m_TerritoryMatrix );
m_TerritoryMatrix = 0;
}
for( size_t i=0; i<m_Territories.size(); i++)
delete m_Territories[i];
m_Territories.clear();
}
void CTerritoryManager::Initialize()
{
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
m_TilesPerSide = terrain->GetVerticesPerSide() - 1;
m_TerritoryMatrix = (CTerritory***) matrix_alloc( m_TilesPerSide, m_TilesPerSide, sizeof(CTerritory*) );
Recalculate();
}
void CTerritoryManager::Recalculate()
{
// Delete any territories created last time we called Recalculate()
for( size_t i=0; i<m_Territories.size(); i++)
{
if( m_Territories[i]->centre )
m_Territories[i]->centre->m_associatedTerritory = 0;
delete m_Territories[i];
}
m_Territories.clear();
// First, find all the units that are territory centres
std::vector<CEntity*> centres;
std::vector<CEntity*> entities;
// g_EntityManager.GetExtant(entities);
for( size_t i=0; i<entities.size(); i++ )
{
if( !entities[i]->entf_get(ENTF_DESTROYED) && entities[i]->m_base->m_isTerritoryCentre )
centres.push_back(entities[i]);
}
const size_t mapSize = m_TilesPerSide * CELL_SIZE;
// If there aren't any centre objects, create one big Gaia territory which spans the whole map
if( centres.empty() )
{
std::vector<CVector2D> boundary;
boundary.push_back( CVector2D(0, 0) );
boundary.push_back( CVector2D(0, mapSize) );
boundary.push_back( CVector2D(mapSize, mapSize) );
boundary.push_back( CVector2D(mapSize, 0) );
CTerritory* ter = new CTerritory( g_Game->GetPlayer(0), HEntity(), boundary );
m_Territories.push_back(ter);
for( size_t x=0; x<m_TilesPerSide; x++ )
{
for( size_t z=0; z<m_TilesPerSide; z++ )
{
m_TerritoryMatrix[x][z] = ter;
}
}
}
else
{
// For each centre object, create a territory
for( size_t i=0; i<centres.size(); i++ )
{
std::vector<CVector2D> boundary;
CalculateBoundary( centres, i, boundary );
CTerritory* ter = new CTerritory( centres[i]->GetPlayer(), centres[i]->me, boundary );
centres[i]->m_associatedTerritory = ter;
m_Territories.push_back(ter);
}
// For each tile, match it to the closest centre object to it.
// TODO: Optimize this, for example by intersecting scanlines with the Voronoi polygons.
for( size_t x=0; x<m_TilesPerSide; x++ )
{
for( size_t z=0; z<m_TilesPerSide; z++ )
{
CVector2D tileLoc( (x+0.5f) * CELL_SIZE, (z+0.5f) * CELL_SIZE );
float bestSquareDist = 1e20f;
for( size_t i=0; i<centres.size(); i++ )
{
CVector2D centreLoc( centres[i]->m_position.X, centres[i]->m_position.Z );
float squareDist = (centreLoc - tileLoc).length2();
if( squareDist < bestSquareDist )
{
bestSquareDist = squareDist;
m_TerritoryMatrix[x][z] = m_Territories[i];
}
}
}
}
}
}
void CTerritoryManager::DelayedRecalculate()
{
// This is useful particularly for Atlas, which wants to recalculate
// the boundaries as you move an object around but which doesn't want
// to waste time recalculating multiple times per frame
m_DelayedRecalculate = true;
}
CTerritory* CTerritoryManager::GetTerritory(int x, int z)
{
debug_assert( (size_t) x < m_TilesPerSide && (size_t) z < m_TilesPerSide );
return m_TerritoryMatrix[x][z];
}
CTerritory* CTerritoryManager::GetTerritory(float x, float z)
{
ssize_t ix, iz;
CTerrain::CalcFromPosition(x, z, ix, iz);
return GetTerritory((int)ix, (int)iz);
}
// Calculate the boundary points of a given territory into the given vector
void CTerritoryManager::CalculateBoundary( std::vector<CEntity*>& centres, size_t myIndex, std::vector<CVector2D>& boundary )
{
// Start with a boundary equal to the whole map
const size_t mapSize = m_TilesPerSide * CELL_SIZE;
boundary.push_back( CVector2D(0, 0) );
boundary.push_back( CVector2D(0, mapSize) );
boundary.push_back( CVector2D(mapSize, mapSize) );
boundary.push_back( CVector2D(mapSize, 0) );
// Clip this polygon against the perpendicular bisector between this centre and each other territory centre
CVector2D myPos( centres[myIndex]->m_position.X, centres[myIndex]->m_position.Z );
for( size_t i=0; i<centres.size(); i++ )
{
if( i != myIndex )
{
CVector2D itsPos( centres[i]->m_position.X, centres[i]->m_position.Z );
CVector2D midpoint = (myPos + itsPos) / 2.0f;
CVector2D normal = itsPos - myPos;
// Clip our polygon to the negative side of the half-space with normal "normal"
// containing point "midpoint", i.e. the side of the perpendicular bisector
// between myPos and itsPos that contains myPos. We do this by tracing around
// the polygon looking at each vertex to decide which ones to add as follows:
// - If a vertex is inside the half-space, take it.
// - If a vertex is inside but the next one is outside, also take the
// intersection of that edge with the perpendicular bisector.
// - If a vertex is outside but the next one is inside, take the
// intersection of that edge with the perpendicular bisector.
std::vector<CVector2D> newBoundary;
for( size_t j=0; j<boundary.size(); j++ )
{
CVector2D& pos = boundary[j];
float dot = (pos - midpoint).Dot(normal);
bool inside = dot < 0.0f;
size_t nextJ = (j+1) % boundary.size(); // index of next point
CVector2D& nextPos = boundary[nextJ];
float nextDot = (nextPos - midpoint).Dot(normal);
bool nextInside = nextDot < 0.0f;
if( inside )
{
newBoundary.push_back( pos );
if( !nextInside )
{
// Also add intersection of this line segment and the bisector
float t = nextDot / (-dot + nextDot);
newBoundary.push_back( pos * t + nextPos * (1.0f - t) );
}
}
else if( nextInside )
{
// Add intersection of this line segment and the bisector
float t = nextDot / (-dot + nextDot);
newBoundary.push_back( pos * t + nextPos * (1.0f - t) );
}
}
boundary = newBoundary;
}
}
}
void CTerritoryManager::RenderTerritories()
{
PROFILE( "render territories" );
if (m_DelayedRecalculate)
{
Recalculate();
m_DelayedRecalculate = false;
}
glDisable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_LINE_SMOOTH);
glLineWidth(1.5f);
CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
CFrustum frustum = g_Game->GetView()->GetCamera()->GetFrustum();
std::vector<CTerritory*>::iterator terr=m_Territories.begin();
for ( ; terr != m_Territories.end(); ++terr )
{
float r, g, b;
if ( (*terr)->owner->GetPlayerID() == 0 )
{
// Use a dark gray for Gaia territories since white looks a bit weird
//glColor3f( 0.65f, 0.65f, 0.65f );
r = g = b = 0.65f;
}
else
{
// Use the player's colour
const SPlayerColour& col = (*terr)->owner->GetColour();
//glColor3f(col.r, col.g, col.b);
r = col.r;
g = col.g;
b = col.b;
}
for ( size_t edge=0; edge < (*terr)->boundary.size(); edge++ )
{
const std::vector<CVector3D>& coords = (*terr)->GetEdgeCoords(edge);
CVector3D start = coords[0];
CVector3D end = coords[coords.size() - 1];
if ( !frustum.DoesSegmentIntersect(start, end) )
continue;
glBegin( GL_LINE_STRIP );
for( size_t i=0; i<coords.size(); i++ )
{
float losScale = 0.0f;
ELOSStatus los = losMgr->GetStatus(coords[i].X, coords[i].Z, g_Game->GetLocalPlayer());
if( los & LOS_VISIBLE ) losScale = 1.0f;
else if( los & LOS_EXPLORED ) losScale = 0.7f;
glColor3f( r*losScale, g*losScale, b*losScale );
glVertex3f( coords[i].X, coords[i].Y, coords[i].Z );
}
glEnd();
}
}
glEnable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
glLineWidth(1.0f);
glColor4f(1,1,1,1);
}
const std::vector<CVector3D>& CTerritory::GetEdgeCoords(size_t edge)
{
if ( edgeCoords.size() == 0 )
{
// Edge coords have not been calculated - calculate them now
edgeCoords.resize( boundary.size() );
const CTerrain* pTerrain = g_Game->GetWorld()->GetTerrain();
// Tweak the boundary to shift all edges "inwards" by 0.3 units towards the territory's centre,
// so that boundaries for adjacent territories don't overlap
std::vector<CVector2D> tweakedBoundary = boundary;
for ( size_t i=0; i<boundary.size(); i++ )
{
size_t prevI = (i+boundary.size()-1) % boundary.size();
size_t nextI = (i+1) % boundary.size();
// Figure out the direction perpendicular to each of the two edges that meet at this point.
CVector2D dir1 = (boundary[i]-boundary[prevI]).beta().Normalize();
CVector2D dir2 = (boundary[nextI]-boundary[i]).beta().Normalize();
// If you draw a picture of what our point looks like and what the two lines 0.3 units
// away from it look like, and draw a line between our point and that one as well as
// drop perpendicular lines from it to the original edges, you get this formula for the
// length and direction we have to be moved.
float angle = acosf(dir1.Dot(dir2));
tweakedBoundary[i] += (dir1 + dir2).Normalize() * 0.3f / cosf(angle/2);
}
// Calculate the heights at points TERRITORY_PRECISION_STEP apart on our edges
// and store the final vertices in edgeCoords.
for ( size_t e=0; e<boundary.size(); e++ )
{
std::vector<CVector3D>& coords = edgeCoords[e];
CVector2D start = tweakedBoundary[e];
CVector2D end = tweakedBoundary[(e+1) % boundary.size()];
float iterf = (end - start).Length() / TERRITORY_PRECISION_STEP;
for ( float i=0; i < iterf; i += TERRITORY_PRECISION_STEP )
{
CVector2D pos = Interpolate( start, end, i/iterf );
coords.push_back( CVector3D( pos.x, pTerrain->GetExactGroundLevel(pos)+0.25f, pos.y ) );
}
coords.push_back( CVector3D( end.x, pTerrain->GetExactGroundLevel(end)+0.25f, end.y ) );
}
}
return edgeCoords[edge];
}
void CTerritory::ClearEdgeCache()
{
edgeCoords.clear();
edgeCoords.resize( boundary.size() );
}

View File

@ -1,82 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// TerritoryManager.h
//
// Calculates territory boundaries and maintains territory data.
//
// Usage:
#ifndef INCLUDED_TERRITORYMANAGER
#define INCLUDED_TERRITORYMANAGER
#include "ps/Vector2D.h"
#include "maths/Vector3D.h"
#include "EntityHandles.h"
class CUnit;
class CPlayer;
const float TERRITORY_PRECISION_STEP = 1.0f;
class CTerritory
{
public:
CPlayer* owner; // owner of the territory, or Gaia for none
HEntity centre; // centre object of this territory
std::vector<CVector2D> boundary; // boundary polygon, in map coordinates
private:
// cached coordinates for the polygon's edge segments (conformed to the terrain)
std::vector<std::vector<CVector3D> > edgeCoords;
public:
CTerritory(CPlayer* owner_, HEntity centre_, std::vector<CVector2D> boundary_)
: owner(owner_), centre(centre_), boundary(boundary_) {}
const std::vector<CVector3D>& GetEdgeCoords(size_t edge);
void ClearEdgeCache();
};
class CTerritoryManager
{
std::vector<CTerritory*> m_Territories;
CTerritory*** m_TerritoryMatrix; // m_TerritoryMatrix[x][z] points to the territory for tile (x, z)
size_t m_TilesPerSide;
bool m_DelayedRecalculate;
public:
CTerritoryManager();
~CTerritoryManager();
void Initialize(); // initialize, called after the game is fully loaded
void Recalculate(); // recalculate the territory boundaries
void DelayedRecalculate(); // recalculate the territory boundaries when next rendered
void RenderTerritories();
CTerritory* GetTerritory(int x, int z); // get the territory to which the given tile belongs
CTerritory* GetTerritory(float x, float z); // get the territory to which the given world-space point belongs
std::vector<CTerritory*>& GetTerritories() { return m_Territories; }
private:
void CalculateBoundary( std::vector<CEntity*>& centres, size_t index, std::vector<CVector2D>& boundary );
};
#endif

View File

@ -1,504 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "TriggerManager.h"
#include "scripting/ScriptingHost.h"
#include "ps/XML/Xeromyces.h"
#include "ps/XML/XeroXMB.h"
#include "ps/CLogger.h"
#define LOG_CATEGORY L"Triggers"
CTrigger::CTrigger()
{
m_timeLeft = m_timeDelay = m_maxRunCount = m_runCount = m_active = 0;
}
CTrigger::~CTrigger()
{
}
CTrigger::CTrigger(const CStrW& name, bool active, float delay, int maxRuns, const CStrW& condFunc,
const CStrW& effectFunc)
{
m_name = name;
m_active = active;
m_timeLeft = m_timeDelay = delay;
m_runCount = 0;
m_maxRunCount = maxRuns;
CStrW validName = name;
validName.Replace(L" ", L"_");
m_conditionFuncString = condFunc;
m_effectFuncString = effectFunc;
m_conditionFunction.Compile( L"TriggerCondition_" + validName, condFunc );
m_effectFunction.Compile( L"TriggerEffect_" + validName, effectFunc );
}
CTrigger::CTrigger(const CStrW& name, bool active, float delay, int maxRuns,
CScriptObject& condFunc, CScriptObject& effectFunc)
{
m_name = name;
m_active = active;
m_timeLeft = m_timeDelay = delay;
m_runCount = 0;
m_maxRunCount = maxRuns;
m_conditionFunction = condFunc;
m_effectFunction = effectFunc;
}
JSBool CTrigger::Construct( JSContext* cx, JSObject* UNUSED(obj), uintN argc, jsval* argv, jsval* rval )
{
JSU_REQUIRE_PARAMS_CPP(6);
CScriptObject condFunc( JS_ValueToFunction(cx, argv[4]) );
CScriptObject effectFunc( JS_ValueToFunction(cx, argv[5]) );
CTrigger* newTrigger = new CTrigger( ToPrimitive<CStr>( argv[0] ),
ToPrimitive<bool>( argv[1] ),
ToPrimitive<float>( argv[2] ),
ToPrimitive<int>( argv[3]),
condFunc, effectFunc );
g_TriggerManager.AddTrigger(newTrigger);
*rval = OBJECT_TO_JSVAL( newTrigger->GetScript() );
return JS_TRUE;
}
CTrigger& CTrigger::operator= (const CTrigger& trigger)
{
m_name = trigger.m_name;
m_active = trigger.m_active;
m_timeLeft = trigger.m_timeLeft;
m_timeDelay = trigger.m_timeDelay;
m_maxRunCount = trigger.m_maxRunCount;
m_conditionFunction = trigger.m_conditionFunction;
m_effectFunction = trigger.m_effectFunction;
return *this;
}
void CTrigger::ScriptingInit()
{
AddProperty<int>(L"runCount", &CTrigger::m_runCount, true);
AddProperty(L"name", &CTrigger::m_name, true);
AddProperty<int>(L"maxRunCount", &CTrigger::m_maxRunCount);
AddProperty<float>(L"timeDelay", &CTrigger::m_timeDelay);
AddMethod<void, &CTrigger::Activate>( "activate", 0 );
AddMethod<void, &CTrigger::Deactivate>( "deactivate", 0 );
CJSObject<CTrigger>::ScriptingInit("Trigger", CTrigger::Construct, 6);
}
bool CTrigger::ShouldFire()
{
if ( !m_active )
return false;
return m_conditionFunction.Run( this->GetScript() );
}
bool CTrigger::Fire()
{
m_effectFunction.Run( this->GetScript() );
++m_runCount;
m_timeLeft = m_timeDelay;
return (m_runCount < m_maxRunCount);
}
void CTrigger::Activate(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
m_active = true;
}
void CTrigger::Deactivate(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
{
m_active = false;
}
void TriggerParameter::SetWindowData(const CStrW& _windowType, CStrW& windowPosition, CStrW& windowSize)
{
windowPosition.Remove( L" " );
windowSize.Remove( L" " );
xPos = windowPosition.BeforeFirst( L"," ).ToInt();
yPos = windowPosition.AfterFirst( L"," ).ToInt();
xSize = windowSize.BeforeFirst( L"," ).ToInt();
ySize = windowSize.AfterFirst( L"," ).ToInt();
windowType = _windowType;
}
bool TriggerParameter::operator< ( const TriggerParameter& rhs ) const
{
if ( row < rhs.row )
return true;
else if ( row == rhs.row)
return ( column < rhs.column );
return false;
}
//===========Trigger Manager===========
CTriggerManager::CTriggerManager() : m_UpdateRate(.20f), m_UpdateTime(.20f)
{
}
CTriggerManager::~CTriggerManager()
{
DestroyEngineTriggers();
}
void CTriggerManager::DestroyEngineTriggers()
{
for ( TriggerIter it = m_TriggerMap.begin(); it != m_TriggerMap.end(); ++it )
{
SAFE_DELETE(it->second);
}
m_TriggerMap.clear();
}
std::vector<std::wstring> CTriggerManager::GetTriggerChoices(const std::wstring& name)
{
return m_TriggerChoices[name];
}
std::vector<std::wstring> CTriggerManager::GetTriggerTranslations(const std::wstring& name)
{
return m_TriggerTranslations[name];
}
void CTriggerManager::Update(float delta_ms)
{
float delta = delta_ms / 1000.f;
m_UpdateTime -= delta;
if ( m_UpdateTime < 0 )
m_UpdateTime += m_UpdateRate;
else
return;
std::list<TriggerIter> expired;
for ( TriggerIter it = m_TriggerMap.begin(); it != m_TriggerMap.end(); ++it )
{
if ( it->second->ShouldFire() )
{
it->second->m_timeLeft -= m_UpdateRate;
if ( it->second->m_timeLeft < 0 )
{
if ( !it->second->Fire() )
expired.push_back(it);
}
}
}
//Remove all expired triggers
for ( std::list<TriggerIter>::iterator it = expired.begin(); it != expired.end(); ++it )
{
delete (*it)->second;
m_TriggerMap.erase(*it);
}
}
void CTriggerManager::AddTrigger(CTrigger* trigger)
{
m_TriggerMap[trigger->GetName()] = trigger;
}
void CTriggerManager::AddGroup(const MapTriggerGroup& group)
{
m_GroupList.push_back(group);
}
void CTriggerManager::SetAllGroups(const std::list<MapTriggerGroup>& groups)
{
m_GroupList = groups;
}
void CTriggerManager::AddTrigger(MapTriggerGroup& group, const MapTrigger& trigger)
{
CStrW conditionBody;
CStrW linkLogic[] = { L"", L" && ", L" || " };
size_t i=0;
bool allParameters = true;
if(trigger.conditions.size() == 0) {
conditionBody = L"return ( true );";
}
else {
conditionBody = L"return ( ";
for ( std::list<MapTriggerCondition>::const_iterator it = trigger.conditions.begin();
it != trigger.conditions.end(); ++it, ++i )
{
//Opening parenthesis here?
std::set<MapTriggerLogicBlock>::const_iterator blockIt;
if ( ( blockIt = trigger.logicBlocks.find(MapTriggerLogicBlock(i)) ) != trigger.logicBlocks.end() )
{
if ( blockIt->negated )
conditionBody += L"!";
conditionBody += L" (";
}
if ( it->negated )
conditionBody += L"!";
conditionBody += it->functionName;
conditionBody += L"(";
for ( std::list<CStrW>::const_iterator it2 = it->parameters.begin(); it2 !=
it->parameters.end(); ++it2 )
{
size_t params = (size_t)std::find(m_ConditionSpecs.begin(), m_ConditionSpecs.end(), it->displayName)->funcParameters;
size_t distance = std::distance(it->parameters.begin(), it2);
//Parameters end here, additional "parameters" are used directly as script
if ( distance == params )
{
conditionBody += L") ";
allParameters = false;
}
//Take display parameter and translate into JS usable code...evilness
CTriggerSpec spec = *std::find( m_ConditionSpecs.begin(), m_ConditionSpecs.end(), it->displayName );
const std::set<TriggerParameter>& specParameters = spec.GetParameters();
//Don't use specialized find, since we're searching for a different member
std::set<TriggerParameter>::const_iterator specParam = std::find(
specParameters.begin(), specParameters.end(), (int)distance);
std::wstring combined = std::wstring( it->functionName + specParam->name );
size_t translatedIndex = std::distance( m_TriggerChoices[combined].begin(),
std::find(m_TriggerChoices[combined].begin(), m_TriggerChoices[combined].end(), std::wstring(*it2)) );
if ( m_TriggerTranslations[combined].empty() )
conditionBody += *it2;
else
conditionBody += m_TriggerTranslations[combined][translatedIndex];
if ( distance + 1 < params )
conditionBody += L", ";
}
if ( allParameters ) //Otherwise, closed inside loop
conditionBody += L")";
if ( trigger.logicBlockEnds.find(i) != trigger.logicBlockEnds.end() )
conditionBody += L" )";
if ( std::distance(it, trigger.conditions.end()) != 1 )
conditionBody += linkLogic[it->linkLogic];
}
conditionBody += L" );";
}
CStrW effectBody;
for ( std::list<MapTriggerEffect>::const_iterator it = trigger.effects.begin();
it != trigger.effects.end(); ++it )
{
effectBody += it->functionName;
effectBody += L"(";
for ( std::list<CStrW>::const_iterator it2 = it->parameters.begin(); it2 !=
it->parameters.end(); ++it2 )
{
size_t distance = std::distance(it->parameters.begin(), it2);
//Take display parameter and translate into JS usable code...evilness
CTriggerSpec spec = *std::find( m_EffectSpecs.begin(), m_EffectSpecs.end(), it->displayName );
const std::set<TriggerParameter>& specParameters = spec.GetParameters();
//Don't use specialized find, since we're searching for a different member
std::set<TriggerParameter>::const_iterator specParam = std::find(
specParameters.begin(), specParameters.end(), (int)distance);
std::wstring combined = std::wstring( it->functionName + specParam->name );
size_t translatedIndex = std::distance( m_TriggerChoices[combined].begin(),
std::find(m_TriggerChoices[combined].begin(), m_TriggerChoices[combined].end(), std::wstring(*it2)) );
if ( m_TriggerTranslations[combined].empty() )
effectBody += *it2;
else
effectBody += m_TriggerTranslations[combined][translatedIndex];
std::list<CStrW>::const_iterator endIt = it->parameters.end();
if ( std::distance(it2, endIt) != 1 )
effectBody += L", ";
}
effectBody += L");";
}
group.triggers.push_back(trigger);
CTrigger* newTrigger = new CTrigger(trigger.name, trigger.active, trigger.timeValue,
trigger.maxRunCount, conditionBody, effectBody);
AddTrigger(newTrigger);
}
//XML stuff
bool CTriggerManager::LoadXml( const VfsPath& pathname )
{
CXeromyces XeroFile;
if ( XeroFile.Load( pathname ) != PSRETURN_OK )
return false;
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
EL(condition);
EL(effect);
EL(definitions);
#undef EL
#undef AT
//Return false on any error to be extra sure the point gets across (FIX IT)
XMBElement root = XeroFile.GetRoot();
if ( root.GetNodeName() != el_definitions )
return false;
XERO_ITER_EL(root, rootChild)
{
int name = rootChild.GetNodeName();
if ( name == el_condition )
{
if ( !LoadTriggerSpec(rootChild, XeroFile, true) )
{
LOG(CLogger::Error, LOG_CATEGORY, L"Error detected in Trigger XML <condition> tag. File: %ls", pathname.string().c_str());
return false;
}
}
else if ( name == el_effect )
{
if ( !LoadTriggerSpec(rootChild, XeroFile, false) )
{
LOG(CLogger::Error, LOG_CATEGORY, L"Error detected in Trigger XML <effect> tag. File: %ls", pathname.string().c_str());
return false;
}
}
else
{
LOG(CLogger::Error, LOG_CATEGORY, L"Invalid tag in trigger XML. File: %ls", pathname.string().c_str());
return false;
}
}
return true;
}
bool CTriggerManager::LoadTriggerSpec( XMBElement condition, CXeromyces& XeroFile, bool isCondition )
{
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
EL(parameter);
EL(window);
EL(inputtype);
EL(parameterorder);
EL(windowrow);
EL(choices);
EL(choicetranslation);
AT(type);
AT(position);
AT(name);
AT(size);
AT(function);
AT(funcParameters);
#undef EL
#undef AT
CTriggerSpec specStore;
specStore.functionName = CStrW( condition.GetAttributes().GetNamedItem(at_function) );
specStore.displayName = CStrW( condition.GetAttributes().GetNamedItem(at_name) );
specStore.funcParameters = CStr( condition.GetAttributes().GetNamedItem(at_funcParameters) ).ToInt();
int row = -1, column = -1;
XERO_ITER_EL(condition, child)
{
int childID = child.GetNodeName();
if ( childID == el_windowrow )
{
++row;
column = -1;
XERO_ITER_EL(child, windowChild)
{
++column;
TriggerParameter specParam(row, column);
specParam.name = windowChild.GetAttributes().GetNamedItem(at_name);
childID = windowChild.GetNodeName();
if ( childID != el_parameter)
return false;
XERO_ITER_EL(windowChild, parameterChild)
{
childID = parameterChild.GetNodeName();
if ( childID == el_window )
{
CStrW type( parameterChild.GetAttributes().GetNamedItem(at_type) );
CStrW pos( parameterChild.GetAttributes().GetNamedItem(at_position) );
CStrW size( parameterChild.GetAttributes().GetNamedItem(at_size) );
specParam.SetWindowData(type, pos, size);
}
else if ( childID == el_inputtype )
specParam.inputType = CStrW( parameterChild.GetText() );
else if ( childID == el_parameterorder )
specParam.parameterOrder = CStrW( parameterChild.GetText() ).ToInt();
else if ( childID == el_choices )
{
std::vector<std::wstring> choices;
CStrW comma(L","), input(parameterChild.GetText());
CStrW substr;
while ( (substr = input.BeforeFirst(comma)) != input )
{
choices.push_back(substr);
input = input.AfterFirst(comma);
}
choices.push_back(substr); //Last element has no comma
m_TriggerChoices[specStore.functionName + specParam.name] = choices;
}
else if ( childID == el_choicetranslation )
{
std::vector<std::wstring> choices;
CStrW comma(L","), input(parameterChild.GetText());
CStrW substr;
while ( (substr = input.BeforeFirst(comma)) != input )
{
choices.push_back(substr);
input = input.AfterFirst(comma);
}
choices.push_back(substr); //Last element has no comma
m_TriggerTranslations[specStore.functionName + specParam.name] = choices;
}
else
return false;
}
specStore.AddParameter(specParam);
}
}
else
return false;
}
if ( isCondition )
m_ConditionSpecs.push_back(specStore);
else
m_EffectSpecs.push_back(specStore);
return true;
}

View File

@ -1,238 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
//========================================================
//Description: Manages loading trigger specs (format in Atlas) and updating trigger objects
//=======================================================
#ifndef INCLUDED_TRIGGERMANAGER
#define INCLUDED_TRIGGERMANAGER
#include "ps/Singleton.h"
#include "scripting/ScriptableObject.h"
#include "simulation/ScriptObject.h"
#include <list>
#include <set>
class CXeromyces;
class XMBElement;
//Trigger storage - used be Atlas to keep track of different triggers
struct MapTriggerCondition
{
MapTriggerCondition() : linkLogic(0), negated(false) { }
CStrW name, functionName, displayName;
std::list<CStrW> parameters;
int linkLogic; //0 = NONE, 1 = AND, 2 = OR
bool negated;
};
struct MapTriggerEffect
{
CStrW name, functionName, displayName;
std::list<CStrW> parameters;
};
struct MapTriggerLogicBlock
{
MapTriggerLogicBlock(size_t i, bool _not=false) : index(i), negated(_not) { }
size_t index;
bool negated;
bool operator< (const MapTriggerLogicBlock& block) const { return (index < block.index); }
bool operator== (const MapTriggerLogicBlock& block) const { return (index == block.index); }
};
struct MapTrigger
{
MapTrigger() : active(false), maxRunCount(0), timeValue(0) { }
bool active;
int maxRunCount;
float timeValue;
CStrW name, groupName;
std::set<MapTriggerLogicBlock> logicBlocks; //Indices of where "(" 's go before
std::set<size_t> logicBlockEnds; //Indices of where ")" 's come after
std::list<MapTriggerCondition> conditions;
std::list<MapTriggerEffect> effects;
void AddLogicBlock(bool negated) { logicBlocks.insert( MapTriggerLogicBlock(conditions.size(), negated) ); }
void AddLogicBlockEnd() { logicBlockEnds.insert( effects.size() ); }
};
struct MapTriggerGroup
{
MapTriggerGroup() { }
MapTriggerGroup(const CStrW& _name, const CStrW& _parentName) : name(_name), parentName(_parentName) {}
std::list<MapTrigger> triggers;
std::list<CStrW> childGroups; //Indices of children
CStrW name, parentName;
bool operator== (const CStrW& _name) const { return (name == _name); }
bool operator== (const MapTriggerGroup& group) const { return (name == group.name); }
};
struct CopyIfRootChild
{
CopyIfRootChild(std::list<MapTriggerGroup>& groupList) : m_groupList(groupList) {}
void operator() ( const MapTriggerGroup& group )
{
if ( group.parentName == L"Triggers" )
m_groupList.push_back(group);
}
private:
void operator= (const CopyIfRootChild& UNUSED(group)) const {}
std::list<MapTriggerGroup>& m_groupList;
};
//Triggers used by engine
class CTrigger : public CJSObject<CTrigger>
{
int m_runCount;
CStrW m_conditionFuncString, m_effectFuncString, m_name, m_groupName;
CScriptObject m_effectFunction;
CScriptObject m_conditionFunction;
public:
CTrigger();
CTrigger(const CStrW& name, bool active, float delay, int maxRuns, const CStrW& condFunc,
const CStrW& effectFunc);
CTrigger(const CStrW& name, bool active, float delay, int maxRuns,
CScriptObject& condFunc, CScriptObject& effectFunc);
CTrigger& operator= (const CTrigger& trigger);
~CTrigger();
void SetFunctionBody(const CStrW& body);
const CStrW& GetConditionString() { return m_conditionFuncString; }
const CStrW& GetEffectString() { return m_effectFuncString; }
const CStrW& GetName() { return m_name; }
const CStrW& GetGroupName() { return m_groupName; }
bool ShouldFire();
//Returns false if trigger exceeds run count
bool Fire();
void Activate(JSContext* cx, uintN argc, jsval* argv);
void Deactivate(JSContext* cx, uintN argc, jsval* argv);
static JSBool Construct( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval );
static void ScriptingInit();
JSContext* m_cx;
bool m_active;
//Changeable by Atlas or needed by manager, no additional affects required
int m_maxRunCount;
float m_timeLeft, m_timeDelay;
};
//*****Holds information specifying how conditions and effects should look like in atlas******
struct TriggerParameter
{
TriggerParameter() {}
TriggerParameter(int _row, int _column) : row(_row), column(_column) {}
void SetWindowData(const CStrW& _windowType, CStrW& windowPosition, CStrW& windowSize);
//Sort parameters in the order they will be added to a sizer in atlas
bool operator< ( const TriggerParameter& rhs ) const;
bool operator< ( const int parameter ) const { return parameterOrder < parameter; }
bool operator== ( const int parameter ) const { return parameterOrder == parameter; }
CStrW name, windowType, inputType;
int row, column, xPos, yPos, xSize, ySize, parameterOrder;
};
class CTriggerSpec
{
public:
CTriggerSpec() {}
~CTriggerSpec() {}
void AddParameter(const TriggerParameter& param) { parameters.insert(param); }
const std::set<TriggerParameter>& GetParameters() const { return parameters; }
int funcParameters;
CStrW displayName, functionName;
bool operator== (const std::wstring& display) const { return display == displayName; }
private:
//Sorted by parameter order to make things easy for Atlas
std::set<TriggerParameter> parameters;
};
typedef CTriggerSpec CTriggerEffect;
typedef CTriggerSpec CTriggerCondition;
/******************************Trigger manager******************************/
class CTriggerManager : public Singleton<CTriggerManager>
{
public:
typedef std::map<CStrW, CTrigger*>::iterator TriggerIter ;
CTriggerManager();
~CTriggerManager();
//Returns false on detection of error
bool LoadXml( const VfsPath& filename );
void Update(float delta);
//Add simulation trigger (probably only called from JS)
void AddTrigger(CTrigger* trigger);
void AddTrigger(MapTriggerGroup& group, const MapTrigger& trigger);
void AddGroup(const MapTriggerGroup& group);
void SetAllGroups(const std::list<MapTriggerGroup>& groups);
void DestroyEngineTriggers();
const std::list<CTriggerCondition>& GetAllConditions() const { return m_ConditionSpecs; }
const std::list<CTriggerEffect>& GetAllEffects() const { return m_EffectSpecs; }
const std::list<MapTriggerGroup>& GetAllTriggerGroups() const { return m_GroupList; }
std::vector<std::wstring> GetTriggerChoices(const std::wstring& name);
std::vector<std::wstring> GetTriggerTranslations(const std::wstring& name);
std::map<CStrW, CTrigger*> m_TriggerMap; //Simulation triggers - used in engine
private:
bool LoadTriggerSpec( XMBElement condition, CXeromyces& XeroFile, bool isCondition );
//Contains choices for trigger choice box parameters, with key = spec.funcName+paramName
std::map<std::wstring, std::vector<std::wstring> > m_TriggerChoices;
std::map<std::wstring, std::vector<std::wstring> > m_TriggerTranslations;
//Holds information which descibes trigger layout in atlas
std::list<MapTriggerGroup> m_GroupList;
std::list<CTriggerCondition> m_ConditionSpecs;
std::list<CTriggerEffect> m_EffectSpecs;
float m_UpdateRate; //TODO: Get this from a config setting
float m_UpdateTime;
};
#define g_TriggerManager CTriggerManager::GetSingleton()
#endif //ifndef INCLUDED_TRIGGERMANAGER