diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp index b89e44e09c..95273cb829 100755 --- a/source/graphics/GameView.cpp +++ b/source/graphics/GameView.cpp @@ -5,6 +5,7 @@ #include "GameView.h" #include "Game.h" #include "Camera.h" +#include "Interact.h" #include "Matrix3D.h" #include "Renderer.h" @@ -66,6 +67,8 @@ CGameView::CGameView(CGame *pGame): m_Camera.m_Orientation.RotateY(DEGTORAD(-45)); m_Camera.m_Orientation.Translate (100, 150, -100); g_Renderer.SetCamera(m_Camera); + + ONCE( ScriptingInit(); ); } CGameView::~CGameView() @@ -73,6 +76,13 @@ CGameView::~CGameView() UnloadResources(); } +void CGameView::ScriptingInit() +{ + AddMethod("startCustomSelection", 0); + AddMethod("endCustomSelection", 0); + + CJSObject::ScriptingInit("GameView"); +} int CGameView::Initialize(CGameAttributes *pAttribs) { @@ -691,3 +701,15 @@ int game_view_handler(const SDL_Event* ev) return EV_PASS; } + +bool CGameView::JSI_StartCustomSelection(JSContext* context, unsigned int argc, jsval* argv) +{ + StartCustomSelection(); + return true; +} + +bool CGameView::JSI_EndCustomSelection(JSContext* context, unsigned int argc, jsval* argv) +{ + ResetInteraction(); + return true; +} diff --git a/source/graphics/GameView.h b/source/graphics/GameView.h index 5d261db69c..a76a061537 100755 --- a/source/graphics/GameView.h +++ b/source/graphics/GameView.h @@ -4,6 +4,8 @@ #include "Camera.h" #include "Vector3D.h" +#include "scripting/ScriptableObject.h" + class CGame; class CGameAttributes; class CWorld; @@ -12,7 +14,7 @@ class CUnitManager; class CProjectileManager; class CModel; -class CGameView +class CGameView: public CJSObject { CGame *m_pGame; CWorld *m_pWorld; @@ -56,6 +58,12 @@ class CGameView // UnloadResources(): Unload all graphics resources loaded by InitResources void UnloadResources(); + + // JS Interface + bool JSI_StartCustomSelection(JSContext *cx, uintN argc, jsval *argv); + bool JSI_EndCustomSelection(JSContext *cx, uintN argc, jsval *argv); + + static void ScriptingInit(); public: CGameView(CGame *pGame); diff --git a/source/graphics/ObjectBase.cpp b/source/graphics/ObjectBase.cpp index f0c2bd9d2c..256a71758e 100644 --- a/source/graphics/ObjectBase.cpp +++ b/source/graphics/ObjectBase.cpp @@ -308,7 +308,7 @@ bool CObjectBase::Load(const char* filename) } else { - LOG(ERROR, LOG_CATEGORY, "Invalid actor format (unrecognised root element '%s')", XeroFile.getElementString(root.getNodeName())); + LOG(ERROR, LOG_CATEGORY, "Invalid actor format (unrecognised root element '%s')", XeroFile.getElementString(root.getNodeName()).c_str()); return false; } diff --git a/source/graphics/ObjectBase.h b/source/graphics/ObjectBase.h index 974fbc6a4d..a3de09cedc 100644 --- a/source/graphics/ObjectBase.h +++ b/source/graphics/ObjectBase.h @@ -5,6 +5,7 @@ class CModel; class CSkeletonAnim; #include +#include #include "CStr.h" class CObjectBase diff --git a/source/graphics/ObjectEntry.cpp b/source/graphics/ObjectEntry.cpp index 7546635925..e7fd4cce65 100755 --- a/source/graphics/ObjectEntry.cpp +++ b/source/graphics/ObjectEntry.cpp @@ -17,8 +17,9 @@ #include "lib/res/vfs.h" -#define LOG_CATEGORY "graphics" +#include +#define LOG_CATEGORY "graphics" CObjectEntry::CObjectEntry(int type, CObjectBase* base) : m_Model(0), m_Type(type), m_Base(base), m_Color(1.0f, 1.0f, 1.0f, 1.0f) diff --git a/source/graphics/ObjectManager.cpp b/source/graphics/ObjectManager.cpp index 13c00fae41..b62b068db4 100755 --- a/source/graphics/ObjectManager.cpp +++ b/source/graphics/ObjectManager.cpp @@ -101,7 +101,8 @@ CObjectEntry* CObjectManager::FindObject(const char* objname) CObjectBase::variation_key var; base->CalculateVariation(choices, var); - return FindObjectVariation(base, var, var.begin()); + CObjectBase::variation_key::iterator vars_it=var.begin(); + return FindObjectVariation(base, var, vars_it); } CObjectEntry* CObjectManager::FindObjectVariation(const char* objname, CObjectBase::variation_key vars, CObjectBase::variation_key::iterator& vars_it) diff --git a/source/graphics/ObjectManager.h b/source/graphics/ObjectManager.h index c289376d09..e7efc5ddc5 100755 --- a/source/graphics/ObjectManager.h +++ b/source/graphics/ObjectManager.h @@ -31,7 +31,7 @@ class CObjectManager : public Singleton public: struct ObjectKey { - ObjectKey(CStr& name, CObjectBase::variation_key& var) + ObjectKey(const CStr& name, const CObjectBase::variation_key& var) : ActorName(name), ActorVariation(var) {} CStr ActorName; diff --git a/source/gui/CGUIList.h b/source/gui/CGUIList.h index f4452cef75..f50b9a1e03 100644 --- a/source/gui/CGUIList.h +++ b/source/gui/CGUIList.h @@ -2,7 +2,7 @@ #ifndef CGUIList_H #define CGUIList_H -#include "GUIText.h" +#include "GUItext.h" class CGUIList { @@ -16,4 +16,4 @@ public: // struct:ish (but for consistency I call it _C_GUIList, and std::vector m_Items; }; -#endif \ No newline at end of file +#endif diff --git a/source/gui/CList.cpp b/source/gui/CList.cpp index 871b81e50f..2d1b2d243e 100644 --- a/source/gui/CList.cpp +++ b/source/gui/CList.cpp @@ -93,7 +93,7 @@ void CList::SetupText() // Generate texts float buffered_y = 0.f; - + for (int i=0; i<(int)pList->m_Items.size(); ++i) { // Create a new SGUIText. Later on, input it using AddText() @@ -107,7 +107,7 @@ void CList::SetupText() AddText(text); } - m_ItemsYPositions[i] = buffered_y; + m_ItemsYPositions[pList->m_Items.size()] = buffered_y; //if (! scrollbar) // CalculateTextPosition(m_CachedActualSize, m_TextPos, *m_GeneratedTexts[0]); diff --git a/source/lib/debug.cpp b/source/lib/debug.cpp index de1deb6ae9..7338d596be 100644 --- a/source/lib/debug.cpp +++ b/source/lib/debug.cpp @@ -17,6 +17,7 @@ #include "precompiled.h" +#include #include #include "lib.h" diff --git a/source/lib/lockfree.cpp b/source/lib/lockfree.cpp index 2a404394c3..8906d1e553 100644 --- a/source/lib/lockfree.cpp +++ b/source/lib/lockfree.cpp @@ -18,6 +18,7 @@ #include "precompiled.h" +#include #include #include "lib.h" @@ -30,7 +31,6 @@ #define PERFORM_SELF_TEST 0 #endif - /* liberties taken: - R(H) will remain constant @@ -973,4 +973,4 @@ static int dummy = run_tests(); #endif // #if PERFORM_SELF_TEST -} // namespace test \ No newline at end of file +} // namespace test diff --git a/source/lib/posix.h b/source/lib/posix.h index c724ad641c..38412fd4ed 100755 --- a/source/lib/posix.h +++ b/source/lib/posix.h @@ -55,6 +55,13 @@ need only be renamed (e.g. _open, _stat). #else +// unix/linux/glibc/gcc says that this macro has to be defined when including +// stdint.h from C++ for stdint.h to define SIZE_MAX and friends +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +#include #include #include #include diff --git a/source/lib/posix_types.h b/source/lib/posix_types.h index 2b448d063f..708aaab19f 100644 --- a/source/lib/posix_types.h +++ b/source/lib/posix_types.h @@ -10,5 +10,10 @@ #ifdef _WIN32 # include "sysdep/win/wposix_types.h" #else +// unix/linux/glibc/gcc says that this macro has to be defined when including +// stdint.h from C++ for stdint.h to define SIZE_MAX and friends +# ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS +# endif # include #endif // #ifdef _WIN32 diff --git a/source/lib/res/file.cpp b/source/lib/res/file.cpp index 1aed248f27..e87a05b460 100755 --- a/source/lib/res/file.cpp +++ b/source/lib/res/file.cpp @@ -25,6 +25,7 @@ #include "detect.h" #include "adts.h" #include "sysdep/sysdep.h" +#include "byte_order.h" #include #include diff --git a/source/lib/res/vfs.cpp b/source/lib/res/vfs.cpp index 2d0e693651..8f5d64890a 100755 --- a/source/lib/res/vfs.cpp +++ b/source/lib/res/vfs.cpp @@ -32,11 +32,11 @@ #include #include +#include #include #include #include - // currently not thread safe. will have to change that if // a prefetch thread is to be used. // not safe to call before main! @@ -294,7 +294,7 @@ struct DirAndPath : dir(d), path(p) {} }; -typedef std::deque DirQueue; +typedef std::deque DirQueue; // passed through TDir::addR's file_enum to dirent_cb diff --git a/source/lib/res/vfs_path.cpp b/source/lib/res/vfs_path.cpp index b79065c059..fb199899e9 100644 --- a/source/lib/res/vfs_path.cpp +++ b/source/lib/res/vfs_path.cpp @@ -119,7 +119,7 @@ bool path_component_valid(const char* name) // convenience function -inline void path_copy(char* dst, const char* src) +void path_copy(char* dst, const char* src) { strcpy_s(dst, VFS_MAX_PATH, src); } diff --git a/source/lib/res/vfs_path.h b/source/lib/res/vfs_path.h index d66f3e5de1..31ad0e433b 100644 --- a/source/lib/res/vfs_path.h +++ b/source/lib/res/vfs_path.h @@ -26,4 +26,4 @@ extern int path_append(char* dst, const char* path1, const char* path2); // used when converting VFS <--> real paths. extern int path_replace(char* dst, const char* src, const char* remove, const char* replace); -#endif // #ifndef VFS_UTIL_H__ \ No newline at end of file +#endif // #ifndef VFS_UTIL_H__ diff --git a/source/lib/res/vfs_tree.cpp b/source/lib/res/vfs_tree.cpp index 24eba75d80..4c02a7fc4a 100644 --- a/source/lib/res/vfs_tree.cpp +++ b/source/lib/res/vfs_tree.cpp @@ -215,7 +215,7 @@ public: { public: typedef std::forward_iterator_tag iterator_category; - typedef T T; + typedef ::T T; typedef T value_type; typedef ptrdiff_t difference_type; typedef const T* pointer; @@ -623,14 +623,14 @@ void tree_init() tree_root_dir->init(); } -inline void tree_clear() +void tree_clear() { tree_root_dir->clearR(); } // write a representation of the VFS tree to stdout. -inline void tree_display() +void tree_display() { tree_root_dir->displayR(0); } diff --git a/source/lib/sysdep/cpu.h b/source/lib/sysdep/cpu.h index 101161431e..2924ea439e 100755 --- a/source/lib/sysdep/cpu.h +++ b/source/lib/sysdep/cpu.h @@ -1,3 +1,6 @@ +#ifndef SYSDEP_CPU_H +#define SYSDEP_CPU_H + #ifdef __cplusplus extern "C" { #endif @@ -40,3 +43,5 @@ extern void serialize(); #ifdef __cplusplus } #endif + +#endif diff --git a/source/lib/sysdep/gfx.h b/source/lib/sysdep/gfx.h index e5f1bc2994..e0134fba2a 100755 --- a/source/lib/sysdep/gfx.h +++ b/source/lib/sysdep/gfx.h @@ -20,4 +20,4 @@ extern char gfx_drv_ver[GFX_DRV_VER_LEN]; // default: "" // or we want more detailed info). gfx_card[] is unchanged on failure. extern void get_gfx_info(void); -#endif // #ifndef GFX_H__ \ No newline at end of file +#endif // #ifndef GFX_H__ diff --git a/source/lib/sysdep/ia32.cpp b/source/lib/sysdep/ia32.cpp index dcd90dd8e8..d121b1629a 100755 --- a/source/lib/sysdep/ia32.cpp +++ b/source/lib/sysdep/ia32.cpp @@ -17,8 +17,6 @@ #include "precompiled.h" -#ifdef _M_IX86 - #include "lib.h" #include "posix.h" #include "ia32.h" @@ -37,6 +35,7 @@ #include #include +#ifndef __GNUC__ // replace pathetic MS libc implementation #ifdef _WIN32 @@ -616,4 +615,49 @@ void serialize() __asm cpuid } -#endif // #ifndef _M_IX86 +#else // #ifndef __GNUC__ + +bool CAS_(uintptr_t* location, uintptr_t expected, uintptr_t new_value) +{ + uintptr_t prev; + + assert2(location >= (uintptr_t *)0x10000); + + __asm__ __volatile__("lock; cmpxchgl %1,%2" + : "=a"(prev) // %0: Result in eax should be stored in prev + : "q"(new_value), // %1: new_value -> e[abcd]x + "m"(*location), // %2: Memory operand + "0"(expected) // Stored in same place as %0 + : "memory"); // We make changes in memory + return prev==expected; +} + +void atomic_add(intptr_t *location, intptr_t increment) +{ + __asm__ __volatile__ ( + "cmpb $1, %1;" + "je 1f;" + "lock;" + "1: addl %3, %0" + : "=m" (*location) /* %0: Output into *location */ + : "m" (cpus), /* %1: Input for cpu check */ + "m" (*location), /* %2: *location is also an input */ + "r" (increment) /* %3: Increment (store in register) */ + : "memory"); /* clobbers memory (*location) */ +} + +void mfence() +{ + // no cpu caps stored in gcc compiles, so we can't check for SSE2 support + /* + if (ia32_cap(SSE2)) + __asm__ __volatile__ ("mfence"); + */ +} + +void serialize() +{ + __asm__ __volatile__ ("cpuid"); +} + +#endif // #ifdef __GNUC__ diff --git a/source/lib/sysdep/ia32.h b/source/lib/sysdep/ia32.h index 509dd11767..4c23788cee 100755 --- a/source/lib/sysdep/ia32.h +++ b/source/lib/sysdep/ia32.h @@ -18,8 +18,8 @@ #ifndef IA32_H #define IA32_H -#ifndef _M_IX86 -#error "including ia32.h without _M_IX86 defined" +#if !(defined(__GNUC__) && defined(i386)) && !defined(_M_IX86) +#error "including ia32.h without _M_IX86 (or both __GNUC__ and i386) defined" #endif #include "lib/types.h" diff --git a/source/lib/sysdep/unix/dir_watch_fam.cpp b/source/lib/sysdep/unix/dir_watch_fam.cpp index d6cddbb251..21212b2f3a 100755 --- a/source/lib/sysdep/unix/dir_watch_fam.cpp +++ b/source/lib/sysdep/unix/dir_watch_fam.cpp @@ -13,13 +13,18 @@ static bool initialized=false; static std::map dirs; +void fam_deinit() +{ + FAMClose(&fc); +} + int dir_add_watch(const char* const n_full_path, intptr_t* const watch) { if(!initialized) { CHECK_ERR(FAMOpen2(&fc, "lib_res")); - atexit2((void*)FAMClose, (uintptr_t)&fc); initialized = true; + atexit(fam_deinit); } FAMRequest req; diff --git a/source/lib/sysdep/unix/udbg.cpp b/source/lib/sysdep/unix/udbg.cpp index 9f7be40c4c..572ba46323 100755 --- a/source/lib/sysdep/unix/udbg.cpp +++ b/source/lib/sysdep/unix/udbg.cpp @@ -4,6 +4,7 @@ #include "timer.h" #include "sysdep/sysdep.h" +#include #include #include #include @@ -387,4 +388,4 @@ void debug_printf(const char* fmt, ...) vprintf(fmt, args); va_end(args); fflush(stdout); -} \ No newline at end of file +} diff --git a/source/lib/sysdep/unix/x.cpp b/source/lib/sysdep/unix/x.cpp index 88d28ebfe9..0cbf315626 100755 --- a/source/lib/sysdep/unix/x.cpp +++ b/source/lib/sysdep/unix/x.cpp @@ -24,6 +24,7 @@ #include #include "lib.h" +#include "sysdep/debug.h" #include "detect.h" #include @@ -152,7 +153,7 @@ wchar_t *clipboard_get() &len, &bytes_left, &data); if (result != Success) - debug_out("clipboard_get: result: %d type:%d len:%d format:%d bytes_left:%d\n", + debug_printf("clipboard_get: result: %d type:%d len:%d format:%d bytes_left:%d\n", result, (int)type, len, format, bytes_left); if (result == Success && bytes_left > 0) { @@ -163,8 +164,8 @@ wchar_t *clipboard_get() if (result == Success) { - debug_out("clipboard_get: XGetWindowProperty succeeded, returning data\n"); - debug_out("clipboard_get: data was: \"%s\", type was %d, XA_STRING atom is %d\n", data, type, XA_STRING); + debug_printf("clipboard_get: XGetWindowProperty succeeded, returning data\n"); + debug_printf("clipboard_get: data was: \"%s\", type was %d, XA_STRING atom is %d\n", data, type, XA_STRING); if (type == XA_STRING) //Latin-1: Just copy into low byte of wchar_t { @@ -177,7 +178,7 @@ wchar_t *clipboard_get() } else { - debug_out("clipboard_get: XGetWindowProperty failed!\n"); + debug_printf("clipboard_get: XGetWindowProperty failed!\n"); return NULL; } } diff --git a/source/main.cpp b/source/main.cpp index f6e0f2d1ad..db2dd679af 100755 --- a/source/main.cpp +++ b/source/main.cpp @@ -806,6 +806,8 @@ TIMER(InitScripting) g_ScriptingHost.DefineConstant( "ORDER_PATROL", CEntityOrder::ORDER_PATROL ); g_ScriptingHost.DefineConstant( "ORDER_ATTACK", CEntityOrder::ORDER_ATTACK_MELEE ); g_ScriptingHost.DefineConstant( "ORDER_GATHER", CEntityOrder::ORDER_GATHER ); + + CNetMessage::ScriptingInit(); JSI_Camera::init(); JSI_Console::init(); diff --git a/source/ps/ConfigDB.cpp b/source/ps/ConfigDB.cpp index 237420132b..ce62b1530d 100755 --- a/source/ps/ConfigDB.cpp +++ b/source/ps/ConfigDB.cpp @@ -134,9 +134,9 @@ namespace ConfigNamespace_JS } JSFunctionSpec Funcs[] = { - { "WriteFile", WriteFile, 2, 0, 0}, - { "Reload", Reload, 0, 0, 0}, - { "SetFile", SetFile, 1, 0, 0}, + { "writeFile", WriteFile, 2, 0, 0}, + { "reload", Reload, 0, 0, 0}, + { "setFile", SetFile, 1, 0, 0}, {0} }; }; diff --git a/source/ps/Game.cpp b/source/ps/Game.cpp index 69edf95053..793159dd06 100755 --- a/source/ps/Game.cpp +++ b/source/ps/Game.cpp @@ -11,177 +11,6 @@ CGame *g_Game=NULL; -/* -<<<<<<< .mine -namespace PlayerArray_JS -{ - JSBool GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) - { - CGameAttributes *pInstance=(CGameAttributes *)JS_GetPrivate(cx, obj); - if (!JSVAL_IS_INT(id)) - return JS_FALSE; - uint index=g_ScriptingHost.ValueToInt(id); - - // Clamp to a preset upper bound. - // FIXME I guess we'll ultimately have max players as a config variable - if (pInstance->m_NumPlayers > PS_MAX_PLAYERS) - pInstance->m_NumPlayers = PS_MAX_PLAYERS; - - // Resize array according to new number of players (note that the array - // size will go up to PS_MAX_PLAYERS, but will never be made smaller - - // this to avoid losing player pointers and make sure they are all - // reclaimed in the end - it's just simpler that way ;-) ) - if (pInstance->m_NumPlayers+1 > pInstance->m_Players.size()) - { - for (size_t i=pInstance->m_Players.size();i<=index;i++) - { - CPlayer *pNewPlayer=new CPlayer((uint)i); - pNewPlayer->SetUpdateCallback(pInstance->m_PlayerUpdateCB, pInstance->m_PlayerUpdateCBData); - pInstance->m_Players.push_back(pNewPlayer); - } - } - - if (index > pInstance->m_NumPlayers) - return JS_FALSE; - - *vp=OBJECT_TO_JSVAL(pInstance->m_Players[index]->GetScript()); - return JS_TRUE; - } - - JSBool SetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) - { - return JS_FALSE; - } - - JSClass Class = { - "PlayerArray", JSCLASS_HAS_PRIVATE, - JS_PropertyStub, JS_PropertyStub, - GetProperty, SetProperty, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, JS_FinalizeStub - }; - - JSBool Construct( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval ) - { - if (argc != 0) - return JS_FALSE; - - JSObject *newObj=JS_NewObject(cx, &Class, NULL, obj); - *rval=OBJECT_TO_JSVAL(newObj); - return JS_TRUE; - } -}; - -CGameAttributes::CGameAttributes(): - m_UpdateCB(NULL), - m_MapFile("test01.pmp"), - m_NumPlayers(2) -{ - ONCE( - g_ScriptingHost.DefineCustomObjectType(&PlayerArray_JS::Class, - PlayerArray_JS::Construct, 0, NULL, NULL, NULL, NULL); - - ScriptingInit(); - ); - - m_PlayerArrayJS=g_ScriptingHost.CreateCustomObject("PlayerArray"); - JS_SetPrivate(g_ScriptingHost.GetContext(), m_PlayerArrayJS, this); - - AddSynchedProperty(L"mapFile", &m_MapFile); - AddSynchedProperty(L"numPlayers", &m_NumPlayers); - - m_Players.resize(4); - for (int i=0;i<4;i++) - m_Players[i]=new CPlayer(i); - - m_Players[0]->SetName(L"Gaia"); - m_Players[0]->SetColour(SPlayerColour(0.2f, 0.7f, 0.2f)); - - m_Players[1]->SetName(L"Acumen"); - m_Players[1]->SetColour(SPlayerColour(1.0f, 0.0f, 0.0f)); - - m_Players[2]->SetName(L"Boco the Insignificant"); - m_Players[2]->SetColour(SPlayerColour(0.0f, 0.0f, 1.0f)); - - m_Players[3]->SetName(L"NoMonkey"); - m_Players[3]->SetColour(SPlayerColour(0.5f, 0.0f, 1.0f)); -} - -CGameAttributes::~CGameAttributes() -{ - std::vector::iterator it=m_Players.begin(); - while (it != m_Players.end()) - { - delete *it; - ++it; - } -} - -jsval CGameAttributes::JSGetPlayers() -{ - return OBJECT_TO_JSVAL(m_PlayerArrayJS); -} - -void CGameAttributes::SetValue(CStrW name, CStrW value) -{ - ISynchedJSProperty *prop=GetSynchedProperty(name); - if (prop) - { - prop->FromString(value); - } -} - -void CGameAttributes::Update(CStrW name, ISynchedJSProperty *attrib) -{ - if (m_UpdateCB) - m_UpdateCB(name, attrib->ToString(), m_UpdateCBData); -} - -void CGameAttributes::SetPlayerUpdateCallback(CPlayer::UpdateCallback *cb, void *userdata) -{ - m_PlayerUpdateCB=cb; - m_PlayerUpdateCBData=userdata; - - for (size_t i=0;iSetUpdateCallback(cb, userdata); - } -} - -void CGameAttributes::ScriptingInit() -{ - AddClassProperty(L"players", (GetFn)&CGameAttributes::JSGetPlayers); - CJSObject::ScriptingInit( "Game" ); - -} - -/*void CGameAttributes::CreateJSObject() -{ - CAttributeMap::CreateJSObject(); - - ONCE( - g_ScriptingHost.DefineCustomObjectType(&PlayerArray_JS::Class, - PlayerArray_JS::Construct, 0, NULL, NULL, NULL, NULL); - ); - - m_PlayerArrayJS=g_ScriptingHost.CreateCustomObject("PlayerArray"); - JS_SetPrivate(g_ScriptingHost.GetContext(), m_PlayerArrayJS, this); - int flags=JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT; - JS_DefineProperty(g_ScriptingHost.GetContext(), m_JSObject, "players", - OBJECT_TO_JSVAL(m_PlayerArrayJS), NULL, NULL, flags); -} - -JSBool CGameAttributes::GetJSProperty(jsval id, jsval *ret) -{ - CStr name=g_ScriptingHost.ValueToString(id); - if (name == CStr("players")) - return JS_TRUE; - return CAttributeMap::GetJSProperty(id, ret); -}*//* - -======= ->>>>>>> .r2037 -*/ // Disable "warning C4355: 'this' : used in base member initializer list". // "The base-class constructors and class member constructors are called before // this constructor. In effect, you've passed a pointer to an unconstructed diff --git a/source/ps/Game.h b/source/ps/Game.h index 8f87ce7199..e1c1fc6ca7 100755 --- a/source/ps/Game.h +++ b/source/ps/Game.h @@ -18,59 +18,6 @@ ERROR_GROUP(Game); // This may be overriden by system.cfg ("max_players") #define PS_MAX_PLAYERS 6 -/* -<<<<<<< .mine -namespace PlayerArray_JS -{ - JSBool GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ); -}; - -#define g_GameAttributes CGameAttributes::GetSingleton() -class CGameAttributes: - public CSynchedJSObject, - public Singleton -{ -public: - typedef void (UpdateCallback)(CStrW name, CStrW newValue, void *data); - -private: - friend JSBool PlayerArray_JS::GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ); - - virtual void Update(CStrW name, ISynchedJSProperty *attrib); - - UpdateCallback *m_UpdateCB; - void *m_UpdateCBData; - - CPlayer::UpdateCallback *m_PlayerUpdateCB; - void *m_PlayerUpdateCBData; - - jsval JSGetPlayers(); - -public: - CStrW m_MapFile; - uint m_NumPlayers; - - CGameAttributes(); - virtual ~CGameAttributes(); - - void SetValue(CStrW name, CStrW value); - - inline void SetUpdateCallback(UpdateCallback *cb, void *userdata) - { - m_UpdateCB=cb; - m_UpdateCBData=userdata; - } - - void SetPlayerUpdateCallback(CPlayer::UpdateCallback *cb, void *userdata); - - std::vector m_Players; - JSObject *m_PlayerArrayJS; - - static void ScriptingInit(); -}; -======= ->>>>>>> .r2037 -*/ class CGame { CWorld m_World; diff --git a/source/ps/Interact.cpp b/source/ps/Interact.cpp index 37438d49c7..3b27fbd8fc 100755 --- a/source/ps/Interact.cpp +++ b/source/ps/Interact.cpp @@ -1,4 +1,7 @@ #include "precompiled.h" + +#include "CLogger.h" + #include "Interact.h" #include "Renderer.h" #include "input.h" @@ -22,6 +25,8 @@ extern CStr g_CursorName; static const float SELECT_DBLCLICK_RATE = 0.5f; const int ORDER_DELAY = 5; +bool customSelectionMode=false; + void CSelectedEntities::addSelection( HEntity entity ) { m_group = -1; @@ -355,6 +360,10 @@ CVector3D CSelectedEntities::getGroupPosition( i8 groupid ) void CSelectedEntities::update() { static std::vector lastSelection; + + // Drop out immediately if we're in some special interaction mode + if (customSelectionMode) + return; if( !( m_selected == lastSelection ) ) { @@ -367,17 +376,18 @@ void CSelectedEntities::update() // Can't order anything off the map if( !g_Game->GetWorld()->GetTerrain()->isOnMap( g_Mouseover.m_worldposition ) ) { - m_contextOrder = -1; + m_defaultCommand = -1; return; } - + // Quick count to see which is the modal default order. - int defaultPoll[CEntityOrder::ORDER_LAST]; - std::map defaultCursor[CEntityOrder::ORDER_LAST]; + const int numCommands=NMT_COMMAND_LAST - NMT_COMMAND_FIRST; + int defaultPoll[numCommands]; + std::map defaultCursor[numCommands]; int t, vote; - for( t = 0; t < CEntityOrder::ORDER_LAST; t++ ) + for( t = 0; t < numCommands; t++ ) defaultPoll[t] = 0; std::vector::iterator it; @@ -385,9 +395,9 @@ void CSelectedEntities::update() { CEventTargetChanged evt( g_Mouseover.m_target ); (*it)->DispatchEvent( &evt ); - vote = evt.m_defaultAction; + vote = evt.m_defaultAction - NMT_COMMAND_FIRST; - if( ( vote >= 0 ) && ( vote < CEntityOrder::ORDER_LAST ) ) + if( ( vote >= 0 ) && ( vote < numCommands ) ) { defaultPoll[vote]++; defaultCursor[vote][evt.m_defaultCursor]++; @@ -395,14 +405,14 @@ void CSelectedEntities::update() } vote = -1; - for( t = 0; t < CEntityOrder::ORDER_LAST; t++ ) + for( t = 0; t < numCommands; t++ ) { if( ( vote == -1 ) || ( defaultPoll[t] > defaultPoll[vote] ) ) vote = t; } std::map::iterator itv; - m_contextOrder = vote; + m_defaultCommand = vote + NMT_COMMAND_FIRST; // Now find the most appropriate cursor t = 0; @@ -422,176 +432,6 @@ void CSelectedEntities::update() } -void CSelectedEntities::setContext( int contextOrder ) -{ - assert( isContextValid( contextOrder ) ); - m_contextOrder = contextOrder; -} - -bool CSelectedEntities::nextContext() -{ - // No valid orders? - if( m_contextOrder == -1 ) return( false ); - int t = m_contextOrder + 1; - while( t != m_contextOrder ) - { - if( t == CEntityOrder::ORDER_LAST ) t = 0; - if( isContextValid( t ) ) break; - t++; - } - if( m_contextOrder == t ) - return( false ); - m_contextOrder = t; - return( true ); -} - -bool CSelectedEntities::previousContext() -{ - // No valid orders? - if( m_contextOrder == -1 ) return( false ); - int t = m_contextOrder - 1; - while( t != m_contextOrder ) - { - if( isContextValid( t ) ) break; - if( t == 0 ) t = CEntityOrder::ORDER_LAST; - t--; - } - if( m_contextOrder == t ) - return( false ); - m_contextOrder = t; - return( true ); -} - -bool CSelectedEntities::isContextValid( int contextOrder ) -{ - if( contextOrder == -1 ) return( false ); - - // Can't order anything off the map - if( !g_Game->GetWorld()->GetTerrain()->isOnMap( g_Mouseover.m_worldposition ) ) - return( false ); - - // Check to see if any member of the selection supports this order type. - std::vector::iterator it; - for( it = m_selected.begin(); it < m_selected.end(); it++ ) - if( (*it)->acceptsOrder( contextOrder, g_Mouseover.m_target ) ) - return( true ); - return( false ); -} - -void CSelectedEntities::contextOrder( bool pushQueue ) -{ - CCamera *pCamera=g_Game->GetView()->GetCamera(); - CTerrain *pTerrain=g_Game->GetWorld()->GetTerrain(); - - std::vector::iterator it; - CEntityOrder context, contextRandomized; - (int&)context.m_type = m_contextOrder; - - switch( m_contextOrder ) - { -// PATROL order: temporatily disabled until we define the network command for it - case CEntityOrder::ORDER_PATROL: - case CEntityOrder::ORDER_GOTO: - { - context.m_data[0].location = g_Mouseover.m_worldposition; - break; -/* - CGotoCommand *msg=new CGotoCommand(); - msg->m_Entity=m_selected[0]->me; - msg->m_TargetX=(u32)g_Mouseover.m_worldposition.x; - msg->m_TargetY=(u32)g_Mouseover.m_worldposition.y; - g_Game->GetSimulation()->QueueLocalCommand(msg); - break; -*/ - } - case CEntityOrder::ORDER_ATTACK_MELEE: - { - context.m_data[0].entity = g_Mouseover.m_target; - for( it = m_selected.begin(); it < m_selected.end(); it++ ) - if( (*it)->acceptsOrder( m_contextOrder, g_Mouseover.m_target ) ) - { - if( !pushQueue ) - (*it)->clearOrders(); - (*it)->pushOrder( context ); - } - return; - } - case CEntityOrder::ORDER_GATHER: - { - context.m_data[0].entity = g_Mouseover.m_target; - for( it = m_selected.begin(); it < m_selected.end(); it++ ) - if( (*it)->acceptsOrder( m_contextOrder, g_Mouseover.m_target ) ) - { - if( !pushQueue ) - (*it)->clearOrders(); - (*it)->pushOrder( context ); - } - return; - } - default: - break; - } - - // 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. - - /* (Simon) - Hmm. Disabled in the makeshift transition to Command Message Queueing... - - Ideally, we'd create a Group with the selected entities, then queue a - command with the Group ID as performing entity, with the center as the - target, then let the location randomization be done in the - net command=>ent. order translator (for now, CSimulation::TranslateMessage) - - Unless this is a problem that'll only be around until we make the real - pathfinder? - */ - - float radius = 2.0f * sqrt( (float)m_selected.size() - 1 ); - - float _x, _y; - - - for( it = m_selected.begin(); it < m_selected.end(); it++ ) - if( ( (*it)->GetPlayer() == g_Game->GetLocalPlayer() ) && - ( (*it)->acceptsOrder( m_contextOrder, g_Mouseover.m_target ) ) ) - { - contextRandomized = context; - do - { - _x = (float)( rand() % 20000 ) / 10000.0f - 1.0f; - _y = (float)( rand() % 20000 ) / 10000.0f - 1.0f; - } - while( ( _x * _x ) + ( _y * _y ) > 1.0f ); - - contextRandomized.m_data[0].location.x += _x * radius; - contextRandomized.m_data[0].location.y += _y * radius; - - // Clamp it to within the map, just in case. - float mapsize = (float)g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide() * CELL_SIZE; - - if( contextRandomized.m_data[0].location.x < 0.0f ) - contextRandomized.m_data[0].location.x = 0.0f; - if( contextRandomized.m_data[0].location.x >= mapsize ) - contextRandomized.m_data[0].location.x = mapsize; - if( contextRandomized.m_data[0].location.y < 0.0f ) - contextRandomized.m_data[0].location.y = 0.0f; - if( contextRandomized.m_data[0].location.y >= mapsize ) - contextRandomized.m_data[0].location.y = mapsize; - - if( !pushQueue ) - (*it)->clearOrders(); - - (*it)->pushOrder( contextRandomized ); - } - -} - void CMouseoverEntities::update( float timestep ) { CCamera *pCamera=g_Game->GetView()->GetCamera(); @@ -881,6 +721,58 @@ void CMouseoverEntities::stopBandbox() m_bandbox = false; } +void FireWorldClickEvent(uint button, int clicks) +{ + debug_printf("FireWorldClickEvent: button %d, clicks %d\n", button, clicks); + g_JSGameEvents.FireWorldClick( + button, + clicks, + g_Selection.m_defaultCommand, + -1, // FIXME Secondary command, depends entity scripts etc + g_Mouseover.m_target, + (uint)g_Mouseover.m_worldposition.x, + (uint)g_Mouseover.m_worldposition.y); +} + +void MouseButtonUpHandler(const SDL_Event *ev, int clicks) +{ + FireWorldClickEvent(ev->button.button, clicks); + + switch( ev->button.button ) + { + case SDL_BUTTON_LEFT: + if (customSelectionMode) + break; + + if( g_Mouseover.m_viewall ) + break; + + if( clicks == 2 ) + { + // Double click + g_Mouseover.expandAcrossScreen(); + } + else if( clicks == 3 ) + { + // Triple click + g_Mouseover.expandAcrossWorld(); + } + + g_Mouseover.stopBandbox(); + if( hotkeys[HOTKEY_SELECTION_ADD] ) + { + g_Mouseover.addSelection(); + } + else if( hotkeys[HOTKEY_SELECTION_REMOVE] ) + { + g_Mouseover.removeSelection(); + } + else + g_Mouseover.setSelection(); + break; + } +} + int interactInputHandler( const SDL_Event* ev ) { if (!g_active || !g_Game) @@ -890,13 +782,19 @@ int interactInputHandler( const SDL_Event* ev ) CCamera *pCamera=pView->GetCamera(); CTerrain *pTerrain=g_Game->GetWorld()->GetTerrain(); - static float lastclicktime = 0.0f; - static HEntity lastclickobject; - static u8 clicks = 0; + // One entry for each of five mouse buttons (SDL mouse buttons 1-5, mouse + // buttons over 5 if existant, will be ignored) + static float lastclicktime[5] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + static HEntity lastclickobject[5]; + static u8 clicks[5] = {0, 0, 0, 0, 0}; static u16 button_down_x, button_down_y; + static float button_down_time; static bool button_down = false; - + + if (customSelectionMode && ev->type != SDL_MOUSEBUTTONUP) + return EV_PASS; + switch( ev->type ) { case SDL_HOTKEYDOWN: @@ -909,12 +807,6 @@ int interactInputHandler( const SDL_Event* ev ) if( g_Selection.m_selected.size() ) pView->SetCameraTarget( g_Selection.getSelectionPosition() ); break; - case HOTKEY_CONTEXTORDER_NEXT: - g_Selection.nextContext(); - break; - case HOTKEY_CONTEXTORDER_PREVIOUS: - g_Selection.previousContext(); - break; default: if( ( ev->user.code >= HOTKEY_SELECTION_GROUP_0 ) && ( ev->user.code <= HOTKEY_SELECTION_GROUP_19 ) ) { @@ -964,53 +856,34 @@ int interactInputHandler( const SDL_Event* ev ) } return( EV_HANDLED ); case SDL_MOUSEBUTTONUP: - switch( ev->button.button ) + { + // Assumes SDL button enums in range [1, 5] + int button = ev->button.button - 1; + // Only process buttons within the range for which we have button state + // arrays above. + if (button >= 0 && button < 5) { - case SDL_BUTTON_LEFT: - if( g_Mouseover.m_viewall ) - break; float time; time = (float)get_time(); // Reset clicks counter if too slow or if the cursor's // hovering over something else now. - if( time - lastclicktime >= SELECT_DBLCLICK_RATE ) - clicks = 0; - if( g_Mouseover.m_target != lastclickobject ) - clicks = 0; - clicks++; + if( time - lastclicktime[button] >= SELECT_DBLCLICK_RATE ) + clicks[button] = 0; + if( g_Mouseover.m_target != lastclickobject[button] ) + clicks[button] = 0; + clicks[button]++; - if( clicks == 2 ) - { - // Double click - g_Mouseover.expandAcrossScreen(); - } - else if( clicks == 3 ) - { - // Triple click - g_Mouseover.expandAcrossWorld(); - } - lastclicktime = time; - lastclickobject = g_Mouseover.m_target; + lastclicktime[button] = time; + lastclickobject[button] = g_Mouseover.m_target; - button_down = false; - g_Mouseover.stopBandbox(); - if( hotkeys[HOTKEY_SELECTION_ADD] ) - { - g_Mouseover.addSelection(); - } - else if( hotkeys[HOTKEY_SELECTION_REMOVE] ) - { - g_Mouseover.removeSelection(); - } - else - g_Mouseover.setSelection(); - break; - case SDL_BUTTON_RIGHT: - g_Selection.contextOrder( hotkeys[HOTKEY_ORDER_QUEUE] ); - break; + if (ev->button.button == SDL_BUTTON_LEFT) + button_down = false; + + MouseButtonUpHandler(ev, clicks[button]); } break; + } case SDL_MOUSEBUTTONDOWN: switch( ev->button.button ) { @@ -1018,6 +891,7 @@ int interactInputHandler( const SDL_Event* ev ) button_down = true; button_down_x = ev->button.x; button_down_y = ev->button.y; + button_down_time = get_time(); break; } break; @@ -1056,4 +930,14 @@ bool isMouseoverType( CEntity* ev, void* userdata ) return( true ); } return( false ); -} \ No newline at end of file +} + +void StartCustomSelection() +{ + customSelectionMode = true; +} + +void ResetInteraction() +{ + customSelectionMode = false; +} diff --git a/source/ps/Interact.h b/source/ps/Interact.h index 9820498be2..32ee751ca7 100755 --- a/source/ps/Interact.h +++ b/source/ps/Interact.h @@ -29,14 +29,14 @@ struct CSelectedEntities : public Singleton clearSelection(); m_group = -1; m_group_highlight = -1; - m_contextOrder = -1; + m_defaultCommand = -1; m_selectionChanged = true; } std::vector m_selected; std::vector m_groups[MAX_GROUPS]; i8 m_group, m_group_highlight; bool m_selectionChanged; - int m_contextOrder; + int m_defaultCommand; void addSelection( HEntity entity ); void removeSelection( HEntity entity ); @@ -57,11 +57,6 @@ struct CSelectedEntities : public Singleton CVector3D getGroupPosition( i8 groupid ); void update(); - bool isContextValid( int contextOrder ); - void contextOrder( bool pushQueue = false ); - void setContext( int contextOrder ); - bool nextContext(); - bool previousContext(); void renderSelectionOutlines(); void renderOverlays(); @@ -121,6 +116,9 @@ struct CMouseoverEntities : public Singleton bool isMouseoverType( CEntity* ev, void* userdata ); bool isOnScreen( CEntity* ev, void* userdata ); +void StartCustomSelection(); +void ResetInteraction(); + int interactInputHandler( const SDL_Event* ev ); #define g_Selection CSelectedEntities::GetSingleton() diff --git a/source/ps/Loader.cpp b/source/ps/Loader.cpp index b41893ac49..732491c4d3 100644 --- a/source/ps/Loader.cpp +++ b/source/ps/Loader.cpp @@ -7,7 +7,7 @@ #include "precompiled.h" #include -#include +#include #include "lib.h" // error codes #include "timer.h" @@ -66,7 +66,7 @@ struct LoadRequest } }; -typedef std::deque LoadRequests; +typedef std::deque LoadRequests; static LoadRequests load_requests; // std::accumulate binary op; used by LDR_EndRegistering to sum up all @@ -327,4 +327,4 @@ int LDR_NonprogressiveLoad() CHECK_ERR(ret); // failed; complain } } -} \ No newline at end of file +} diff --git a/source/ps/Network/AllNetMessages.h b/source/ps/Network/AllNetMessages.h index cf904c983b..ad43503eb6 100755 --- a/source/ps/Network/AllNetMessages.h +++ b/source/ps/Network/AllNetMessages.h @@ -56,9 +56,12 @@ enum ENetMessageType NMT_StartGame, /* In-Game Stage */ NMT_EndCommandBatch, - NMT_COMMAND_FIRST, - NMT_GotoCommand=NMT_COMMAND_FIRST, - NMT_COMMAND_LAST=NMT_GotoCommand, + NMT_Goto, NMT_COMMAND_FIRST=NMT_Goto, + NMT_Patrol, + NMT_AddWaypoint, + NMT_AttackMelee, + NMT_Gather, + NMT_COMMAND_LAST, /* Post-Game Stage */ /** @@ -120,6 +123,7 @@ enum #define ALLNETMSGS_DONT_CREATE_NMTS #define START_NMT_CLASS_(_nm) START_NMT_CLASS(C ## _nm, NMT_ ## _nm) +#define DERIVE_NMT_CLASS_(_base, _nm) START_NMT_CLASS_DERIVED(C ## _base, C ## _nm, NMT_ ## _nm) START_NMTS() @@ -198,24 +202,32 @@ START_NMT_CLASS_(EndCommandBatch) NMT_FIELD_INT(m_TurnLength, u32, 2) END_NMT_CLASS() -START_NMT_CLASS_(GotoCommand) - NMT_FIELD(HEntity, m_Entity) +START_NMT_CLASS(CCommand, NMT_NONE) + NMT_FIELD(CEntityList, m_Entities) +END_NMT_CLASS() + +DERIVE_NMT_CLASS_(Command, Goto) NMT_FIELD_INT(m_TargetX, u32, 2) NMT_FIELD_INT(m_TargetY, u32, 2) END_NMT_CLASS() -/* -#define NMT_FIELD_MAPPOS(_nm) NMT_FIELD_INT(_nm##X, u32, 2) NMT_FIELD_INT(_nm##Y, u32, 2) - -START_NMT_CLASS_(SetWaypoint) - NMT_FIELD(HEntity, m_Entity) - NMT_FIELD_MAPPOS(m_Target) +DERIVE_NMT_CLASS_(Command, Patrol) + NMT_FIELD_INT(m_TargetX, u32, 2) + NMT_FIELD_INT(m_TargetY, u32, 2) END_NMT_CLASS() -START_NMT_CLASS_(AddWaypoint) - NMT_FIELD(HEntity, m_Entity) - NMT_FIELD_MAPPOS(m_Target) -END_NMY_CLASS()*/ +DERIVE_NMT_CLASS_(Command, AddWaypoint) + NMT_FIELD_INT(m_TargetX, u32, 2) + NMT_FIELD_INT(m_TargetY, u32, 2) +END_NMT_CLASS() + +DERIVE_NMT_CLASS_(Command, AttackMelee) + NMT_FIELD(HEntity, m_Target) +END_NMT_CLASS() + +DERIVE_NMT_CLASS_(Command, Gather) + NMT_FIELD(HEntity, m_Target) +END_NMT_CLASS() END_NMTS() diff --git a/source/ps/Network/Client.cpp b/source/ps/Network/Client.cpp index e6a01150fd..564e3a26d8 100755 --- a/source/ps/Network/Client.cpp +++ b/source/ps/Network/Client.cpp @@ -324,7 +324,7 @@ bool CNetClient::InGameHandler(CNetMessage *pMsg, CNetSession *pSession) CHAIN(BaseHandler); CHAIN(ChatHandler); - if (msgType >= NMT_COMMAND_FIRST && msgType <= NMT_COMMAND_LAST) + if (msgType >= NMT_COMMAND_FIRST && msgType < NMT_COMMAND_LAST) { pClient->QueueMessage(1, pMsg); TAKEN(pMsg); diff --git a/source/ps/Network/NMTCreator.h b/source/ps/Network/NMTCreator.h index e007494779..30054a9062 100755 --- a/source/ps/Network/NMTCreator.h +++ b/source/ps/Network/NMTCreator.h @@ -9,6 +9,7 @@ #undef START_NMTS #undef END_NMTS #undef START_NMT_CLASS +#undef START_NMT_CLASS_DERIVED #undef NMT_FIELD_INT #undef NMT_FIELD #undef NMT_START_ARRAY @@ -32,18 +33,29 @@ #define START_NMTS() #define END_NMTS() +#define START_NMT_CLASS(_nm, _tp) \ + START_NMT_CLASS_DERIVED(CNetMessage, _nm, _tp) + /** * Start the definition of a network message type. * + * @param _base The name of the base class of the message * @param _nm The name of the class * @param _tp The NetMessageType associated with the class. It is *not* safe to * have several classes with the same value of _tp in the same executable */ -#define START_NMT_CLASS(_nm, _tp) \ +#define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \ CNetMessage *Deserialize##_nm(const u8 *, uint); \ -struct _nm: public CNetMessage \ +class _nm: public _base \ { \ - _nm(): CNetMessage(_tp) {} \ +protected: \ + _nm(ENetMessageType type): _base(type) {}\ + \ + /* This one is for subclasses that want to use the base class' string */ \ + /* converters to get SubMessage { , ... } */ \ + CStr GetStringRaw() const;\ +public: \ + _nm(): _base(_tp) {} \ virtual uint GetSerializedLength() const; \ virtual u8 *Serialize(u8 *buffer) const; \ virtual const u8 *Deserialize(const u8 *pos, const u8 *end); \ @@ -103,9 +115,11 @@ struct _nm: public CNetMessage \ #define END_NMTS() #define START_NMT_CLASS(_nm, _tp) \ + START_NMT_CLASS_DERIVED(CNetMessage, _nm, _tp) +#define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \ uint _nm::GetSerializedLength() const \ { \ - uint ret=0; \ + uint ret=_base::GetSerializedLength(); \ const _nm *thiz=this; #define NMT_START_ARRAY(_nm) \ @@ -140,10 +154,12 @@ uint _nm::GetSerializedLength() const \ #define END_NMTS() #define START_NMT_CLASS(_nm, _tp) \ + START_NMT_CLASS_DERIVED(CNetMessage, _nm, _tp) +#define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \ u8 *_nm::Serialize(u8 *buffer) const \ { \ /*printf("In " #_nm "::Serialize()\n");*/ \ - u8 *pos=buffer; \ + u8 *pos=_base::Serialize(buffer); \ const _nm *thiz=this; #define NMT_START_ARRAY(_nm) \ @@ -181,6 +197,8 @@ u8 *_nm::Serialize(u8 *buffer) const \ #define BAIL_DESERIALIZER return NULL #define START_NMT_CLASS(_nm, _tp) \ + START_NMT_CLASS_DERIVED(CNetMessage, _nm, _tp) +#define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \ CNetMessage *Deserialize##_nm(const u8 *buffer, uint length) \ { \ _nm *ret=new _nm(); \ @@ -191,6 +209,7 @@ CNetMessage *Deserialize##_nm(const u8 *buffer, uint length) \ } \ const u8 *_nm::Deserialize(const u8 *pos, const u8 *end) \ { \ + pos=_base::Deserialize(pos, end); \ _nm *thiz=this; \ /*printf("In Deserialize" #_nm "\n"); */ @@ -229,6 +248,8 @@ const u8 *_nm::Deserialize(const u8 *pos, const u8 *end) \ #define END_NMTS() { NMT_NONE, NULL } }; #define START_NMT_CLASS(_nm, _tp) \ + START_NMT_CLASS_DERIVED(CNetMessage, _nm, _tp) +#define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \ { _tp, Deserialize##_nm }, #define NMT_START_ARRAY(_nm) @@ -254,6 +275,22 @@ const u8 *_nm::Deserialize(const u8 *pos, const u8 *end) \ CStr _nm::GetString() const \ { \ CStr ret=#_nm _T(" { "); \ + return ret + GetStringRaw() + _T(" }"); \ +} \ +CStr _nm::GetStringRaw() const \ +{ \ + CStr ret; \ + const _nm *thiz=this; + +#define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \ +CStr _nm::GetString() const \ +{ \ + CStr ret=#_nm _T(" { "); \ + return ret + GetStringRaw() + _T(" }"); \ +} \ +CStr _nm::GetStringRaw() const \ +{ \ + CStr ret=_base::GetStringRaw() + _T(", "); \ const _nm *thiz=this; #define NMT_START_ARRAY(_nm) \ @@ -281,7 +318,7 @@ CStr _nm::GetString() const \ ret += _T(", "); #define END_NMT_CLASS() \ - return ret.GetSubstring(0, ret.Length()-2)+_T(" }"); \ + return ret.GetSubstring(0, ret.Length()-2); \ } #include "NMTCreator.h" diff --git a/source/ps/Network/NetMessage.cpp b/source/ps/Network/NetMessage.cpp index 2e82441629..99ac6d7b76 100755 --- a/source/ps/Network/NetMessage.cpp +++ b/source/ps/Network/NetMessage.cpp @@ -5,6 +5,8 @@ #include #include +#include "Entity.h" + #define ALLNETMSGS_IMPLEMENT #include "NetMessage.h" @@ -69,3 +71,98 @@ CNetMessage *CNetMessage::DeserializeMessage(ENetMessageType type, u8 *buffer, u return (pDes)(buffer, length); } +void CNetMessage::ScriptingInit() +{ +#define def(_msg) g_ScriptingHost.DefineConstant(#_msg, _msg) + + def(NMT_Goto); + def(NMT_Patrol); + def(NMT_AddWaypoint); + def(NMT_AttackMelee); + def(NMT_Gather); +} + +CCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSContext *cx, uintN argc, jsval *argv) +{ + assert(argc >= 1); + + int msgType; + + try + { + msgType = g_ScriptingHost.ValueToInt( argv[0] ); + } + catch(PSERROR_Scripting_ConversionFailed) + { + JS_ReportError(cx, "Invalid order type"); + return NULL; + } + + #define ArgumentCountError() STMT(\ + JS_ReportError(cx, "Too few parameters!"); \ + return NULL; ) + #define ArgumentTypeError() STMT(\ + JS_ReportError(cx, "Parameter type error!"); \ + return NULL; ) + #define ReadPosition(_msg, _field) \ + try { \ + if (argIndex+2 > argc) \ + ArgumentCountError();\ + if (!JSVAL_IS_INT(argv[argIndex]) || !JSVAL_IS_INT(argv[argIndex+1])) \ + ArgumentTypeError(); \ + _msg->_field ## X = g_ScriptingHost.ValueToInt(argv[argIndex++]); \ + _msg->_field ## Y = g_ScriptingHost.ValueToInt(argv[argIndex++]); \ + } catch (PSERROR_Scripting_ConversionFailed) { \ + JS_ReportError(cx, "Invalid location"); \ + return NULL; \ + } + #define ReadEntity(_msg, _field) \ + STMT(\ + if (argIndex+1 > argc) \ + ArgumentCountError(); \ + if (!JSVAL_IS_OBJECT(argv[argIndex])) \ + ArgumentTypeError(); \ + CEntity *ent=ToNative(argv[argIndex++]); \ + if (!ent) \ + { \ + JS_ReportError(cx, "Invalid entity parameter"); \ + return NULL; \ + } \ + _msg->_field=ent->me; \ + ) + + #define PositionMessage(_msg) \ + case NMT_ ## _msg: \ + { \ + C##_msg *msg = new C##_msg(); \ + msg->m_Entities = entities; \ + ReadPosition(msg, m_Target); \ + return msg; \ + } + + #define EntityMessage(_msg) \ + case NMT_ ## _msg: \ + { \ + C##_msg *msg = new C##_msg(); \ + msg->m_Entities = entities; \ + ReadEntity(msg, m_Target); \ + return msg; \ + } + + // argIndex, incremented by reading macros. We have already "eaten" the + // first argument (message type) + uint argIndex = 1; + switch (msgType) + { + // NMT_Goto, targetX, targetY + PositionMessage(Goto) + PositionMessage(Patrol) + PositionMessage(AddWaypoint) + + EntityMessage(AttackMelee) + EntityMessage(Gather) + default: + JS_ReportError(cx, "Invalid order type"); + return NULL; + } +} diff --git a/source/ps/Network/NetMessage.h b/source/ps/Network/NetMessage.h index e6a0e9cc19..983e245cce 100755 --- a/source/ps/Network/NetMessage.h +++ b/source/ps/Network/NetMessage.h @@ -11,6 +11,9 @@ #include "AllNetMessages.h" #undef ALLNETMSGS_DONT_CREATE_NMTS +class CCommand; +class CEntityList; + /** * The base class for network messages */ @@ -63,6 +66,14 @@ public: * there was an error in data format. */ static CNetMessage *DeserializeMessage(ENetMessageType type, u8 *buffer, uint length); + + /** + * Register a selection of message types as JS constants. + * The constant's names will be the same as those of the enums + */ + static void ScriptingInit(); + + static CCommand *CommandFromJSArgs(const CEntityList &entities, JSContext* cx, uintN argc, jsval* argv); }; typedef CNetMessage * (*NetMessageDeserializer) (const u8 *buffer, uint length); diff --git a/source/ps/Network/ServerSession.cpp b/source/ps/Network/ServerSession.cpp index a72c691677..a0407ca42d 100644 --- a/source/ps/Network/ServerSession.cpp +++ b/source/ps/Network/ServerSession.cpp @@ -182,7 +182,7 @@ bool CNetServerSession::InGameHandler(CNetMessage *pMsg, CNetSession *pNetSessio if (ChatHandler(pMsg, pNetSession)) return true; - if (pMsg->GetType() >= NMT_COMMAND_FIRST && pMsg->GetType() <= NMT_COMMAND_LAST) + if (pMsg->GetType() >= NMT_COMMAND_FIRST && pMsg->GetType() < NMT_COMMAND_LAST) { // All Command Messages (i.e. simulation turn synchronized messages) //pSession->m_pPlayer->ValidateCommand(pMsg); diff --git a/source/ps/Player.cpp b/source/ps/Player.cpp index 50a4617097..37e7df9440 100755 --- a/source/ps/Player.cpp +++ b/source/ps/Player.cpp @@ -40,7 +40,7 @@ void CPlayer::ScriptingInit() // AddClassProperty( L"name", &CPlayer::m_Name ); // AddClassProperty( L"colour", &CPlayer::m_Colour ); - AddProperty( L"controlled", (IJSObject::GetFn)JSI_GetControlledEntities ); + AddProperty( L"controlled", (IJSObject::GetFn)&CPlayer::JSI_GetControlledEntities ); CJSObject::ScriptingInit( "Player" ); } diff --git a/source/ps/Profile.cpp b/source/ps/Profile.cpp index 122d592721..c05c8d40cc 100644 --- a/source/ps/Profile.cpp +++ b/source/ps/Profile.cpp @@ -173,7 +173,7 @@ bool CProfileNode::Return() void CProfileNode::ScriptingInit() { - AddProperty( L"name", (IJSObject::GetFn)CProfileNode::JS_GetName ); + AddProperty( L"name", (IJSObject::GetFn)&CProfileNode::JS_GetName ); /* AddReadOnlyClassProperty( L"callsTotal", &CProfileNode::calls_total ); AddReadOnlyClassProperty( L"callsPerFrame", &CProfileNode::calls_frame_last ); diff --git a/source/ps/ProfileViewer.h b/source/ps/ProfileViewer.h index d848f6c5e7..7c9ac17fa0 100644 --- a/source/ps/ProfileViewer.h +++ b/source/ps/ProfileViewer.h @@ -11,4 +11,4 @@ void ResetProfileViewer(); void RenderProfile(); int profilehandler( const SDL_Event* ev ); -#endif \ No newline at end of file +#endif diff --git a/source/ps/VFSUtil.cpp b/source/ps/VFSUtil.cpp index fb1f92b8c3..6bb58e2aa8 100755 --- a/source/ps/VFSUtil.cpp +++ b/source/ps/VFSUtil.cpp @@ -7,6 +7,8 @@ #include "CLogger.h" #define LOG_CATEGORY "vfs" +#include + using namespace VFSUtil; // Because I'm lazy, and it saves a few lines of code in other places: @@ -73,7 +75,7 @@ int VFSUtil::EnumDirEnts(const CStr start_path, const char* user_filter, // (less stack usage; avoids seeks by reading all entries in a // directory consecutively) - std::deque dir_queue; + std::deque dir_queue; dir_queue.push_back(start_path); // for each directory: diff --git a/source/ps/scripting/JSCollection.h b/source/ps/scripting/JSCollection.h index 2ad10a44d7..6c7c910538 100755 --- a/source/ps/scripting/JSCollection.h +++ b/source/ps/scripting/JSCollection.h @@ -283,7 +283,7 @@ template JSBool CJSCollection::E std::vector* b = RetrieveSet( cx, JSVAL_TO_OBJECT( argv[0] ) ); if( !b ) return( JS_FALSE ); - std::vector::iterator ita, itb; + typename std::vector::iterator ita, itb; size_t seek = a->size(); for( ita = a->begin(); ita != a->end(); ita++ ) diff --git a/source/ps/scripting/JSInterface_Selection.cpp b/source/ps/scripting/JSInterface_Selection.cpp index 16c074a012..855635d94e 100755 --- a/source/ps/scripting/JSInterface_Selection.cpp +++ b/source/ps/scripting/JSInterface_Selection.cpp @@ -91,48 +91,3 @@ JSBool JSI_Selection::setGroups( JSContext* context, JSObject* obj, jsval id, js return( JS_TRUE ); } - -JSBool JSI_Selection::isValidContextOrder( JSContext* context, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval ) -{ - assert( argc >= 1 ); - int orderCode; - try - { - orderCode = g_ScriptingHost.ValueToInt( argv[0] ); - } - catch( ... ) - { - JS_ReportError( context, "Invalid order type" ); - *rval = BOOLEAN_TO_JSVAL( false ); - return( JS_TRUE ); - } - *rval = BOOLEAN_TO_JSVAL( g_Selection.isContextValid( orderCode ) ); - return( JS_TRUE ); -} - -JSBool JSI_Selection::getContextOrder( JSContext* context, JSObject* obj, jsval id, jsval* vp ) -{ - *vp = INT_TO_JSVAL( g_Selection.m_contextOrder ); - return( JS_TRUE ); -} - -JSBool JSI_Selection::setContextOrder( JSContext* context, JSObject* obj, jsval id, jsval* vp ) -{ - int orderCode; - try - { - orderCode = g_ScriptingHost.ValueToInt( *vp ); - } - catch( ... ) - { - JS_ReportError( context, "Invalid order type" ); - return( JS_TRUE ); - } - if( !g_Selection.isContextValid( orderCode ) ) - { - JS_ReportError( context, "Order is not valid in this context: %d", orderCode ); - return( JS_TRUE ); - } - g_Selection.setContext( orderCode ); - return( JS_TRUE ); -} diff --git a/source/ps/scripting/JSInterface_VFS.cpp b/source/ps/scripting/JSInterface_VFS.cpp index 2d7458a8dc..2e23bf7930 100644 --- a/source/ps/scripting/JSInterface_VFS.cpp +++ b/source/ps/scripting/JSInterface_VFS.cpp @@ -1,5 +1,7 @@ #include "precompiled.h" +#include + #include "ps/CStr.h" #include "ps/VFSUtil.h" #include "lib/res/res.h" @@ -7,7 +9,6 @@ #include "scripting/JSConversions.h" #include "scripting/JSInterface_VFS.h" - // shared error handling code #define JS_CHECK_FILE_ERR(err)\ /* this is liable to happen often, so don't complain */\ diff --git a/source/scripting/DOMEvent.cpp b/source/scripting/DOMEvent.cpp index c0c3a1573d..e94b17b2b9 100755 --- a/source/scripting/DOMEvent.cpp +++ b/source/scripting/DOMEvent.cpp @@ -148,7 +148,7 @@ void CScriptEvent::ScriptingInit() AddMethod( "toString", 0 ); AddMethod( "preventDefault", 0 ); AddMethod( "cancel", 0 ); - AddMethod( "stopPropagation", 0 ); + AddMethod( "stopPropagation", 0 ); AddProperty( L"type", &CScriptEvent::m_Type, true ); AddProperty( L"cancelable", &CScriptEvent::m_Cancelable, true ); diff --git a/source/scripting/EventTypes.h b/source/scripting/EventTypes.h index 452d3a4944..2ef337d019 100644 --- a/source/scripting/EventTypes.h +++ b/source/scripting/EventTypes.h @@ -20,6 +20,7 @@ enum EEventType EVENT_GAME_START = 0, EVENT_GAME_TICK, EVENT_SELECTION_CHANGED, + EVENT_WORLD_CLICK, }; // Only used for entity events... @@ -33,4 +34,4 @@ static const wchar_t* EventNames[] = /* EVENT_TARGET_CHANGED */ L"onTargetChanged", /* If this unit is selected and the mouseover object changes */ /* EVENT_PREPARE_ORDER */ L"onPrepareOrder", /* To check if a unit can execute a given order */ /* EVENT_ORDER_TRANSITION */ L"onOrderTransition" /* When we change orders (sometimes...) */ -}; \ No newline at end of file +}; diff --git a/source/scripting/GameEvents.h b/source/scripting/GameEvents.h index 0ab3ab8b14..415801ff86 100644 --- a/source/scripting/GameEvents.h +++ b/source/scripting/GameEvents.h @@ -18,11 +18,45 @@ class CGameEvents : public IEventTarget, public Singleton { bool m_CausedByPlayer; public: - CEventSelectionChanged( bool CausedByPlayer ) : CScriptEvent( L"selectionChanged", EVENT_SELECTION_CHANGED, false ) + CEventSelectionChanged(bool CausedByPlayer): + CScriptEvent( L"selectionChanged", EVENT_SELECTION_CHANGED, false ), + m_CausedByPlayer(CausedByPlayer) { AddLocalProperty( L"byPlayer", &m_CausedByPlayer, true ); } }; + + class CEventWorldClick: public CScriptEvent + { + int m_Button; + int m_Clicks; + int m_Command; + int m_SecondaryCommand; + CEntity *m_Entity; + uint m_X, m_Y; + public: + CEventWorldClick(int button, int clicks, int command, int secCommand, CEntity *ent, uint x, uint y): + CScriptEvent(L"worldClick", EVENT_WORLD_CLICK, false), + m_Button(button), + m_Clicks(clicks), + m_Command(command), + m_SecondaryCommand(secCommand), + m_Entity(ent), + m_X(x), + m_Y(y) + { + AddLocalProperty(L"button", &m_Button); + AddLocalProperty(L"clicks", &m_Clicks); + AddLocalProperty(L"command", &m_Command); + AddLocalProperty(L"secondaryCommand", &m_SecondaryCommand); + if (ent) + AddLocalProperty(L"entity", &m_Entity); + else + AddProperty(L"entity", JSVAL_NULL); + AddLocalProperty(L"x", &m_X); + AddLocalProperty(L"y", &m_Y); + } + }; public: void FireSelectionChanged( bool CausedByPlayer ) @@ -30,8 +64,14 @@ public: CEventSelectionChanged evt( CausedByPlayer ); DispatchEvent( &evt ); } + + void FireWorldClick(int button, int clicks, int command, int secCommand, CEntity *ent, uint x, uint y) + { + CEventWorldClick evt(button, clicks, command, secCommand, ent, x, y); + DispatchEvent(&evt); + } }; #define g_JSGameEvents CGameEvents::GetSingleton() -#endif \ No newline at end of file +#endif diff --git a/source/scripting/ScriptGlue.cpp b/source/scripting/ScriptGlue.cpp index 5d85f0a0b2..c1dac3742e 100755 --- a/source/scripting/ScriptGlue.cpp +++ b/source/scripting/ScriptGlue.cpp @@ -13,6 +13,7 @@ #include "LightEnv.h" #include "MapWriter.h" #include "GameEvents.h" +#include "Interact.h" #include "Game.h" #include "Network/Server.h" @@ -82,6 +83,8 @@ JSFunctionSpec ScriptFunctionTable[] = {"v3dist", v3dist, 2, 0, 0 }, + {"issueCommand", issueCommand, 2, 0, 0 }, + {"exit", exitProgram, 0, 0, 0 }, {"crash", crash, 0, 0, 0 }, {"forceGC", forceGC, 0, 0, 0 }, @@ -108,6 +111,7 @@ JSPropertySpec ScriptGlobalTable[] = { "players", 0, JSPROP_PERMANENT | JSPROP_READONLY, GetPlayerSet, NULL }, { "localPlayer", 0, JSPROP_PERMANENT, GetLocalPlayer, SetLocalPlayer }, { "gaiaPlayer", 0, JSPROP_PERMANENT | JSPROP_READONLY, GetGaiaPlayer, NULL }, + { "gameView", 0, JSPROP_PERMANENT | JSPROP_READONLY, GetGameView, NULL }, { 0, 0, 0, 0, 0 }, }; @@ -238,6 +242,15 @@ JSBool SetLocalPlayer( JSContext* context, JSObject* obj, jsval id, jsval* vp ) return( JS_TRUE ); } +JSBool GetGameView( JSContext* cx, JSObject* globalObject, jsval id, jsval* vp ) +{ + if (g_Game) + *vp = OBJECT_TO_JSVAL( g_Game->GetView()->GetScript() ); + else + *vp = JSVAL_NULL; + return( JS_TRUE ); +} + JSBool AddGlobalHandler( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval ) { *rval = BOOLEAN_TO_JSVAL( g_JSGameEvents.AddHandlerJS( cx, argc, argv ) ); @@ -553,3 +566,30 @@ JSBool _rewriteMaps(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(globa CMapWriter::RewriteAllMaps(g_Game->GetWorld()->GetTerrain(), g_Game->GetWorld()->GetUnitManager(), &g_LightEnv); return JS_TRUE; } + +JSBool issueCommand(JSContext* context, JSObject* UNUSEDPARAM(globalObject), unsigned int argc, jsval* argv, jsval* rval) +{ + assert(argc >= 2); + assert(JSVAL_IS_OBJECT(argv[0])); + + CEntityList entities; + + if (JS_GetClass(JSVAL_TO_OBJECT(argv[0])) == &CEntity::JSI_class) + entities.push_back( (ToNative(argv[0])) ->me); + else + entities = *EntityCollection::RetrieveSet(context, JSVAL_TO_OBJECT(argv[0])); + + CNetMessage *msg=CNetMessage::CommandFromJSArgs(entities, context, argc-1, argv+1); + if (msg) + { + g_Console->InsertMessage(L"issueCommand: %hs", msg->GetString().c_str()); + *rval = g_ScriptingHost.UCStringToValue(msg->GetString()); + + g_Game->GetSimulation()->QueueLocalCommand(msg); + } + else + { + *rval = JSVAL_FALSE; + } + return JS_TRUE; +} diff --git a/source/scripting/ScriptGlue.h b/source/scripting/ScriptGlue.h index bd0b29e110..2101e715e7 100755 --- a/source/scripting/ScriptGlue.h +++ b/source/scripting/ScriptGlue.h @@ -45,6 +45,7 @@ JSFunc getGlobal; JSFunc setCursor; +JSBool GetGameView( JSContext* context, JSObject* globalObject, jsval id, jsval* vp ); JSFunc GetGameObject; JSFunc createServer; JSFunc createClient; @@ -61,6 +62,8 @@ JSFunc getFPS; JSFunc getCursorPosition; JSFunc v3dist; +JSFunc issueCommand; + // Returns a string that says when ScriptGlue.cpp was last recompiled JSFunc buildTime; diff --git a/source/scripting/ScriptableComplex.h b/source/scripting/ScriptableComplex.h index 898225fa80..f509e5985a 100644 --- a/source/scripting/ScriptableComplex.h +++ b/source/scripting/ScriptableComplex.h @@ -8,6 +8,8 @@ #include +#include + #ifndef SCRIPTABLE_COMPLEX_INCLUDED #define SCRIPTABLE_COMPLEX_INCLUDED @@ -958,7 +960,7 @@ template JSPropertySpec CJSComplex::JSI_ }; template std::vector CJSComplex::m_Methods; -template typename CJSComplex::PropertyTable CJSComplex::m_IntrinsicProperties; +template typename CJSComplex::PropertyTable CJSComplex::m_IntrinsicProperties; template bool CJSComplex::GetProperty( JSContext* cx, CStrW PropertyName, jsval* vp ) { diff --git a/source/scripting/ScriptableObject.h b/source/scripting/ScriptableObject.h index 9f774303c7..99c488172c 100755 --- a/source/scripting/ScriptableObject.h +++ b/source/scripting/ScriptableObject.h @@ -143,7 +143,7 @@ public: } void Uproot() { - if( JSVAL_IS_GCTHING( m_Data ) ) + if( JSVAL_IS_GCTHING( m_Data )) JS_RemoveRoot( g_ScriptingHost.GetContext(), (void*)&m_Data ); } jsval Get( JSContext* cx, IJSObject* object ) @@ -439,7 +439,7 @@ template JSPropertySpec CJSObject::JSI_p template std::vector CJSObject::m_Methods; -template typename CJSObject::PropertyTable CJSObject::m_NativeProperties; +template typename CJSObject::PropertyTable CJSObject::m_NativeProperties; #endif diff --git a/source/simulation/BaseEntity.cpp b/source/simulation/BaseEntity.cpp index 9741edc6cc..02c1c3c02d 100755 --- a/source/simulation/BaseEntity.cpp +++ b/source/simulation/BaseEntity.cpp @@ -245,7 +245,7 @@ bool CBaseEntity::loadXML( CStr filename ) CStrW EventName = L"on" + (CStrW)Child.getAttributes().getNamedItem( at_on ); CStrW Code (Child.getText()); - CStrW ExternalFunction = (CStrW)Child.getAttributes().getNamedItem( at_function ); + utf16string ExternalFunction = Child.getAttributes().getNamedItem( at_function ); // Does a property with this name already exist? @@ -253,10 +253,10 @@ bool CBaseEntity::loadXML( CStr filename ) { if( CStrW( EventNames[eventID] ) == EventName ) { - if( ExternalFunction != CStrW() ) + if( ExternalFunction != utf16string() ) { jsval fnval; - JSBool ret = JS_GetUCProperty( g_ScriptingHost.GetContext(), g_ScriptingHost.GetGlobalObject(), ExternalFunction.c_str(), ExternalFunction.Length(), &fnval ); + JSBool ret = JS_GetUCProperty( g_ScriptingHost.GetContext(), g_ScriptingHost.GetGlobalObject(), ExternalFunction.c_str(), ExternalFunction.size(), &fnval ); assert( ret ); JSFunction* fn = JS_ValueToFunction( g_ScriptingHost.GetContext(), fnval ); if( !fn ) @@ -346,7 +346,7 @@ void CBaseEntity::XMLLoadProperty( const CXeromyces& XeroFile, const XMBElement& void CBaseEntity::ScriptingInit() { AddMethod( "toString", 0 ); - AddClassProperty( L"traits.id.classes", (GetFn)getClassSet, (SetFn)setClassSet ); + AddClassProperty( L"traits.id.classes", (GetFn)&CBaseEntity::getClassSet, (SetFn)&CBaseEntity::setClassSet ); CJSComplex::ScriptingInit( "EntityTemplate" ); } diff --git a/source/simulation/Collision.cpp b/source/simulation/Collision.cpp index bcbe0ec7b1..491fe6513b 100755 --- a/source/simulation/Collision.cpp +++ b/source/simulation/Collision.cpp @@ -3,6 +3,8 @@ #include "Collision.h" #include "EntityManager.h" +#include + CBoundingObject* getContainingObject( const CVector2D& point ) { std::vector* entities = g_EntityManager.getExtant(); diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index 8e9788efec..0a466ccb8f 100755 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -648,7 +648,7 @@ void CEntity::ScriptingInit() AddMethod( "getSpawnPoint", 1 ); AddClassProperty( L"template", (CBaseEntity* CEntity::*)&CEntity::m_base, false, (NotifyFn)&CEntity::loadBase ); - AddClassProperty( L"traits.id.classes", (GetFn)getClassSet, (SetFn)setClassSet ); + AddClassProperty( L"traits.id.classes", (GetFn)&CEntity::getClassSet, (SetFn)&CEntity::setClassSet ); CJSComplex::ScriptingInit( "Entity", Construct, 2 ); } diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index c6d76fa188..2f28737b0c 100755 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -106,7 +106,7 @@ public: int m_lastState; // Position in the current state's cycle - static const size_t NOT_IN_CYCLE = -1; + static const size_t NOT_IN_CYCLE = (size_t)-1; size_t m_fsm_cyclepos; // -cycle_length....cycle_length CSkeletonAnim* m_fsm_animation; // the animation we're about to play this cycle, size_t m_fsm_anipos; // the time at which we should start playing it. @@ -197,6 +197,7 @@ public: // Script-bound functions jsval ToString( JSContext* cx, uintN argc, jsval* argv ); + bool Order( JSContext* cx, uintN argc, jsval* argv, bool Queued ); inline bool OrderSingle( JSContext* cx, uintN argc, jsval* argv ) { diff --git a/source/simulation/EntityHandles.cpp b/source/simulation/EntityHandles.cpp index 567a4b8004..6d50da98dc 100755 --- a/source/simulation/EntityHandles.cpp +++ b/source/simulation/EntityHandles.cpp @@ -129,3 +129,57 @@ HEntity::operator CStr() const sprintf(buf, "Entity#%04x", m_handle); return CStr(buf); } + +uint CEntityList::GetSerializedLength() const +{ + return 2*size(); +} + +u8 *CEntityList::Serialize(u8 *buffer) const +{ + for (int i=0;ioperator CStr(); + } + buf += " }"; + return buf; + } +} diff --git a/source/simulation/EntityHandles.h b/source/simulation/EntityHandles.h index 65c1b8e1b0..6f20a9dada 100755 --- a/source/simulation/EntityHandles.h +++ b/source/simulation/EntityHandles.h @@ -24,9 +24,16 @@ #include "Network/Serialization.h" #define INVALID_HANDLE 65535 +// The maximum numerical value of an entity handle sent over the network +#define MAX_HANDLE 0x7fff +// If (handle & PS_ENTITY_SENTINEL_BIT) is non-zero, this is a terminating +// entity id. Reset the sentinel bit to get the original handle. +#define HANDLE_SENTINEL_BIT 0x8000 + class CEntity; class CEntityManager; +class CEntityList; class CStr8; class CHandle @@ -40,6 +47,7 @@ public: class HEntity { friend class CEntityManager; + friend class CEntityList; u16 m_handle; private: void addRef(); @@ -64,4 +72,35 @@ public: operator CStr8() const; }; +/* + CEntityList + + DESCRIPTION: Represents a group of entities that is the target/subject of + a network command. Use for easy serialization of one or more entity IDs. + + SERIALIZED FORMAT: The entities are stored as a sequence of 16-bit ints, the + termination of the list marked by an integer with HANDLE_SENTINEL_BIT + set. The length is thus 2*(number of entities) +*/ +struct CEntityList: public std::vector +{ + // Create an empty list + inline CEntityList() + {} + // Create a list from an existing entity vector + inline CEntityList(const std::vector &vect): + std::vector(vect) + {} + // Create a list containing one entity + inline CEntityList(HEntity oneEntity) + { + push_back(oneEntity); + } + + uint GetSerializedLength() const; + u8 *Serialize(u8 *buffer) const; + const u8 *Deserialize(const u8 *buffer, const u8 *end); + operator CStr8() const; +}; + #endif diff --git a/source/simulation/EntityManager.cpp b/source/simulation/EntityManager.cpp index 118ab0414c..314536e46a 100755 --- a/source/simulation/EntityManager.cpp +++ b/source/simulation/EntityManager.cpp @@ -54,7 +54,7 @@ HEntity CEntityManager::create( CBaseEntity* base, CVector3D position, float ori { assert( base ); if( !base ) - return( HEntity() ); + return HEntity(); while( m_entities[m_nextalloc].m_refcount ) { diff --git a/source/simulation/EntitySupport.h b/source/simulation/EntitySupport.h index 902d8df899..a25c44b10a 100755 --- a/source/simulation/EntitySupport.h +++ b/source/simulation/EntitySupport.h @@ -111,4 +111,4 @@ struct SClassSet } }; -#endif \ No newline at end of file +#endif diff --git a/source/simulation/Projectile.cpp b/source/simulation/Projectile.cpp index 0fd4293543..ae7d898c5c 100644 --- a/source/simulation/Projectile.cpp +++ b/source/simulation/Projectile.cpp @@ -192,10 +192,12 @@ JSBool CProjectile::Construct( JSContext* cx, JSObject* obj, unsigned int argc, if( argc >= 7 ) Miss = argv[6]; // Script to run on impact with the floor. - CProjectile* p = g_ProjectileManager.AddProjectile( Model, Here, There, Speed / 1000.0f, Originator, Impact, Miss ); + { + CProjectile* p = g_ProjectileManager.AddProjectile( Model, Here, There, Speed / 1000.0f, Originator, Impact, Miss ); - *rval = ToJSVal( *p ); - return( JS_TRUE ); + *rval = ToJSVal( *p ); + return( JS_TRUE ); + } fail: *rval = JSVAL_NULL; @@ -263,4 +265,4 @@ void CProjectileManager::InterpolateAll( double relativeOffset ) std::vector::iterator it; for( it = m_Projectiles.begin(); it != m_Projectiles.end(); ++it ) (*it)->Interpolate( absoluteOffset ); -} \ No newline at end of file +} diff --git a/source/simulation/Projectile.h b/source/simulation/Projectile.h index c5b54a883e..bb37307f95 100644 --- a/source/simulation/Projectile.h +++ b/source/simulation/Projectile.h @@ -10,7 +10,7 @@ #include "Vector3D.h" #include "Vector2D.h" #include "Singleton.h" -#include "Scripting/ScriptableObject.h" +#include "scripting/ScriptableObject.h" #include "scripting/DOMEvent.h" #include "ScriptObject.h" diff --git a/source/simulation/Scheduler.cpp b/source/simulation/Scheduler.cpp index 6f0590ec74..34e6a04669 100755 --- a/source/simulation/Scheduler.cpp +++ b/source/simulation/Scheduler.cpp @@ -158,4 +158,4 @@ JSBool CJSProgressTimer::Construct( JSContext* cx, JSObject* obj, unsigned int a *rval = OBJECT_TO_JSVAL( timer->GetScript() ); return( JS_TRUE ); -} \ No newline at end of file +} diff --git a/source/simulation/Scheduler.h b/source/simulation/Scheduler.h index c30f45f45a..38b0d4f163 100755 --- a/source/simulation/Scheduler.h +++ b/source/simulation/Scheduler.h @@ -9,6 +9,8 @@ #define SCHEDULER_INCLUDED #include +#include + #include "EntityMessage.h" #include "EntityHandles.h" #include "Singleton.h" @@ -73,7 +75,7 @@ struct CScheduler : public Singleton class CJSProgressTimer : public CJSObject { - friend CScheduler; + friend struct CScheduler; double m_Max, m_Current, m_Increment; JSFunction* m_Callback; JSObject* m_OperateOn; diff --git a/source/simulation/ScriptObject.cpp b/source/simulation/ScriptObject.cpp index 66afb454f7..62d5cd0ac2 100755 --- a/source/simulation/ScriptObject.cpp +++ b/source/simulation/ScriptObject.cpp @@ -94,7 +94,7 @@ bool CScriptObject::Run( JSObject* Context, jsval* rval, uintN argc, jsval* argv bool CScriptObject::Run( JSObject* Context, uintN argc, jsval* argv ) { jsval Temp; - if( !Run( Context, &Temp ) ) + if( !Run( Context, &Temp, argc, argv ) ) return( false ); return( g_ScriptingHost.ValueToBool( Temp ) ); } diff --git a/source/simulation/Simulation.cpp b/source/simulation/Simulation.cpp index 2c7943040f..e4b92b42dc 100755 --- a/source/simulation/Simulation.cpp +++ b/source/simulation/Simulation.cpp @@ -1,5 +1,7 @@ #include "precompiled.h" +#include + #include #include "Profile.h" @@ -19,6 +21,8 @@ #include "gui/CGUI.h" +using namespace std; + extern CConsole *g_Console; CSimulation::CSimulation(CGame *pGame): @@ -119,21 +123,135 @@ void CSimulation::Simulate() PROFILE_END( "turn manager update" ); } +// 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(CEntityOrder order, const vector &entities, bool clearQueue) +{ + vector::const_iterator it; + float radius = 2.0f * sqrt( (float)entities.size() - 1 ); + + for (it = entities.begin(); it < entities.end(); it++) + { + float _x, _y; + CEntityOrder randomizedOrder = order; + + do + { + _x = (float)( rand() % 20000 ) / 10000.0f - 1.0f; + _y = (float)( rand() % 20000 ) / 10000.0f - 1.0f; + } + while( ( _x * _x ) + ( _y * _y ) > 1.0f ); + + randomizedOrder.m_data[0].location.x += _x * radius; + randomizedOrder.m_data[0].location.y += _y * radius; + + // Clamp it to within the map, just in case. + float mapsize = (float)g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide() * CELL_SIZE; + + if( randomizedOrder.m_data[0].location.x < 0.0f ) + randomizedOrder.m_data[0].location.x = 0.0f; + if( randomizedOrder.m_data[0].location.x >= mapsize ) + randomizedOrder.m_data[0].location.x = mapsize; + if( randomizedOrder.m_data[0].location.y < 0.0f ) + randomizedOrder.m_data[0].location.y = 0.0f; + if( randomizedOrder.m_data[0].location.y >= mapsize ) + randomizedOrder.m_data[0].location.y = mapsize; + + if( clearQueue ) + (*it)->clearOrders(); + + (*it)->pushOrder( randomizedOrder ); + } +} + +void QueueOrder(CEntityOrder order, const vector &entities, bool clearQueue) +{ + vector::const_iterator it; + + for (it = entities.begin(); it < entities.end(); it++) + { + if( clearQueue ) + (*it)->clearOrders(); + + (*it)->pushOrder( order ); + } +} + uint CSimulation::TranslateMessage(CNetMessage *pMsg, uint clientMask, void *userdata) { - CSimulation *pSimulation=(CSimulation *)userdata; - - CEntityOrder entOrder; + CEntityOrder order; + bool clearQueue = true; + +#define ENTITY_POSITION(_msg, _order) do\ + { \ + _msg *msg=(_msg *)pMsg; \ + order.m_type=CEntityOrder::_order; \ + order.m_data[0].location.x=(float)msg->m_TargetX; \ + order.m_data[0].location.y=(float)msg->m_TargetY; \ + RandomizeLocations(order, msg->m_Entities, clearQueue); \ + } while(0) +#define ENTITY_ENTITY(_msg, _order) do\ + { \ + _msg *msg=(_msg *)pMsg; \ + order.m_type=CEntityOrder::_order; \ + order.m_data[0].entity=msg->m_Target; \ + QueueOrder(order, msg->m_Entities, clearQueue); \ + } while(0) + switch (pMsg->GetType()) { - case NMT_GotoCommand: - CGotoCommand *msg=(CGotoCommand *)pMsg; - entOrder.m_type=CEntityOrder::ORDER_GOTO; - entOrder.m_data[0].location.x=(float)msg->m_TargetX; - entOrder.m_data[0].location.y=(float)msg->m_TargetY; - CEntity *ent=msg->m_Entity; - ent->pushOrder( entOrder ); - break; + case NMT_AddWaypoint: + { + CAddWaypoint *msg=(CAddWaypoint *)pMsg; + order.m_type=CEntityOrder::ORDER_LAST; + order.m_data[0].location.x=(float)msg->m_TargetX; + order.m_data[0].location.y=(float)msg->m_TargetY; + vector::iterator it = msg->m_Entities.begin(); + for (;it != msg->m_Entities.end(); ++it) + { + deque::const_iterator ord_it; + ord_it=(*it)->m_orderQueue.end() - 1; + for (;ord_it >= (*it)->m_orderQueue.begin();--ord_it) + { + if (ord_it->m_type == CEntityOrder::ORDER_PATH_END_MARKER) + { + order.m_type = CEntityOrder::ORDER_GOTO; + (*it)->pushOrder(order); + break; + } + if (ord_it->m_type == CEntityOrder::ORDER_PATROL) + { + order.m_type = ord_it->m_type; + (*it)->pushOrder(order); + break; + } + } + if (order.m_type == CEntityOrder::ORDER_LAST) + { + LOG(ERROR, "simulation", "Got an AddWaypoint message for an entity that isn't moving."); + } + } + break; + } + case NMT_Goto: + ENTITY_POSITION(CGoto, ORDER_GOTO); + break; + case NMT_Patrol: + ENTITY_POSITION(CPatrol, ORDER_PATROL); + break; + case NMT_AttackMelee: + ENTITY_ENTITY(CAttackMelee, ORDER_ATTACK_MELEE); + break; + case NMT_Gather: + ENTITY_ENTITY(CGather, ORDER_GATHER); + break; } return clientMask;