diff -r -c stratagus_2.2.7.veryorig/doc/scripts/game.html stratagus_2.2.7.patched/doc/scripts/game.html *** stratagus_2.2.7.veryorig/doc/scripts/game.html 2013-10-03 19:56:26.457550587 +0300 --- stratagus_2.2.7.patched/doc/scripts/game.html 2013-10-12 05:24:06.965840458 +0300 *************** *** 50,55 **** --- 50,56 ---- Briefing CenterMap ChangeUnitsOwner + CommandFunctionalities CreateUnit DebugPrint DefineBurningBuilding *************** *** 64,69 **** --- 65,71 ---- StratagusMap GameCycle GetPlayerData + GetSelection GetThisPlayer GetUnitVariable GetCurrentLuaPath *************** *** 259,264 **** --- 261,350 ---- ChangeUnitsOwner({0, 0}, {10, 10}, 3, 2) + +

CommandFunctionalities (old_destination_x, old_destination_y, unitnumber, totalunits)

+ + Define what the commands move, attack (but not when targetting a unit), attack ground and patrol do. It is called automatically when such an action is performed. + +
+ function CommandFunctionalities (old_destination_x, old_destination_y, unitnumber, totalunits)
+ 	...
+ 	new_destination_x = ...
+ 	new_destination_y = ...
+ 	...
+ 	return new_destination_x, new_destination_y
+ end
+ 
+ + The variables can be renamed. These are just examples. + +
+
old_destination_x
+
This contains the X coordinate of the tile the unit was heading when this function was called. +
+
old_destination_y
+
This contains the Y coordinate of the tile the unit was heading when this function was called. +
+
unitnumber
+
This is the "selection number" of the current unit we are moving at the time this function is called. +
+
totalunits
+
This is the total number of units we were moving when this function was called. +
+
+ +
+
new_destination_x
+
This is the X coordinate of the tile which we want to send this unit instead of its original destination +
+
new_destination_y
+
This is the Y coordinate of the tile which we want to send this unit instead of its original destination +
+
+ +

Example

+ +
+    --The formation system. This function changes the function of commands (move, attack, etc.)
+    --The GetSelection() function is a very useful tool here. It copies IDs of the units that are executing the command to a table called SelectedUnitTable.
+    --Couple that with GetUnitVariable and you can do wonders here.
+ 
+    function CommandFunctionalities (oldx, oldy, unitnumber, totalunits)
+       newx = oldx
+       newy = oldy
+       if unitnumber == 1 then --Only do this once per command
+          GetSelection() -- Form a table called SelectedUnitTable that contains the ID's of units we have selected
+          unitarrangement = {} --Numbers of the selected units
+          tmpattackranges = {} --Attack ranges for sorting
+          for i = 1, #SelectedUnitTable do
+             tmpattackranges[i] = GetUnitVariable(SelectedUnitTable[i], "AttackRange")
+          end
+          table.sort(tmpattackranges) --Sort the attack ranges
+          for i = 1, totalunits do --Assign units to their respective attack range
+             for k = 1, totalunits do
+                if GetUnitVariable(SelectedUnitTable[i], "AttackRange") == tmpattackranges[k] then
+                   unitarrangement[i] = k
+                   tmpattackranges[k] = -1 --Make sure we won't assign a unit to this attack range twice
+                   break
+                end
+             end
+          end
+       end
+ 
+       --Let's make a box formation facing right
+       if unitnumber == 1 then --Do this only once
+          sidelength = math.ceil(math.sqrt(totalunits))
+          offset = math.floor(sidelength / 2)
+       end
+ 
+       newx = newx - math.ceil(unitarrangement[unitnumber]/sidelength) + offset
+ 
+       newy = newy - unitarrangement[unitnumber] % sidelength + offset
+ 
+       return newx, newy --Return the unit's new destination
+    end
+ 
+

CreateUnit(type, player, {x, y})

*************** *** 708,713 **** --- 794,814 ---- + +

GetSelection()

+ + Creates a table called SelectedUnitTable that contains the ID's of units we have selected + +

Example

+ +
+    GetSelection()
+    for i = 1, #SelectedUnitTable do --Create a table containing the attack ranges of our currently selected units.
+       attackranges[i] = GetUnitVariable(SelectedUnitTable[i], "AttackRange")
+    end
+ 
+ +

GetThisPlayer()

diff -r -c stratagus_2.2.7.veryorig/doc/scripts/index.html stratagus_2.2.7.patched/doc/scripts/index.html *** stratagus_2.2.7.veryorig/doc/scripts/index.html 2013-10-03 19:56:26.457550587 +0300 --- stratagus_2.2.7.patched/doc/scripts/index.html 2013-10-12 05:20:36.977840982 +0300 *************** *** 131,136 **** --- 131,138 ----
CheckDependency
+
CommandFunctionalities
+
CreateUnit
DebugPrint
*************** *** 241,246 **** --- 243,250 ----
GetPlayerData
+
GetSelection
+
GetThisPlayer
GetTimer
diff -r -c stratagus_2.2.7.veryorig/src/include/commands.h stratagus_2.2.7.patched/src/include/commands.h *** stratagus_2.2.7.veryorig/src/include/commands.h 2013-10-03 19:56:26.445550587 +0300 --- stratagus_2.2.7.patched/src/include/commands.h 2013-10-11 03:02:04.297980894 +0300 *************** *** 130,135 **** --- 130,137 ---- extern void SendCommandStandGround(CUnit &unit, int flush); /// Send follow command extern void SendCommandFollow(CUnit &unit, CUnit &dest, int flush); + /// Call lua function that defines what commands do (for example, formations) + extern void CommandFunctionalities(const char* command, int selectednumber, CUnit &unit, const Vec2i &pos, int flush); /// Send move command extern void SendCommandMove(CUnit &unit, const Vec2i &pos, int flush); /// Send repair command diff -r -c stratagus_2.2.7.veryorig/src/network/commands.cpp stratagus_2.2.7.patched/src/network/commands.cpp *** stratagus_2.2.7.veryorig/src/network/commands.cpp 2013-10-03 19:56:26.453550587 +0300 --- stratagus_2.2.7.patched/src/network/commands.cpp 2013-10-12 01:38:40.497874193 +0300 *************** *** 42,47 **** --- 42,109 ---- #include "network.h" #include "spells.h" #include "replay.h" + #include "map.h" //It contains map width and height + #include "ui.h" //This defines the LUA functions. There might be a less heavyweight header file but I didn't find one. + + /** + ** Call the lua function that defines what the commands (move, attack, etc.) do. It was designed to be used for formations. + ** + ** @param command The command's name (move, attack, etc.) + ** @param selectednumber "Selection number" of the unit we are moving. We use this instead of our own loop inside this function since the code in mouse.cpp is pretty messy and doing otherwise would break a lot of things easily. + ** @param unit Unit we are moving + ** @param origpos Location we are moving the unit to + */ + void CommandFunctionalities(const char* command, int selectednumber, CUnit &unit, const Vec2i &origpos, int flush) + { + Vec2i newpos = origpos; + int firstoftheselected_x = origpos.x; + int firstoftheselected_y = origpos.y; + + lua_getglobal(Lua, "CommandFunctionalities"); /* function to be called */ + if (lua_isnil(Lua, -1)) { //If CommandFunctionalities LUA function wasn't found, don't do anything special. Just revert to the default system of moving units. + lua_pop(Lua, 1); + } else { + lua_pushnumber(Lua, origpos.x); //Push X coordinate of the original position + lua_pushnumber(Lua, origpos.y); //Push Y coordinate of the original position + lua_pushnumber(Lua, selectednumber + 1); //Push the "selection number" of the unit we are moving + lua_pushnumber(Lua, NumSelected); //Push the total number of moving units + + /* do the call */ + if (lua_pcall(Lua, 4, 2, 0) != 0) + printf("error running function `CommandFunctionalities': %s.", + lua_tostring(Lua, -1)); + + /* retrieve result */ + if (!lua_isnumber(Lua, -1)) + printf("Return value must be a number. In function `CommandFunctionalities'"); + newpos.y = lua_tonumber(Lua, -1); + lua_pop(Lua, 1); /* pop returned value */ + if (!lua_isnumber(Lua, -1)) + printf("Return value must be a number. In function `CommandFunctionalities'"); + newpos.x = lua_tonumber(Lua, -1); + lua_pop(Lua, 1); /* pop returned value */ + } + if (newpos.x < 0) newpos.x = 0; + if (newpos.y < 0) newpos.y = 0; + if (newpos.x >= Map.Info.MapWidth - 1) newpos.x = Map.Info.MapWidth - 1; + if (newpos.y >= Map.Info.MapHeight - 1) newpos.y = Map.Info.MapHeight - 1; + if (strcmp(command, "move") == 0) { + SendCommandMove(unit, newpos, flush); + } + else if (strcmp(command, "attackemptyspace") == 0) { + SendCommandAttack(unit, newpos, NoUnitP, flush); + } + else if (strcmp(command, "attackground") == 0) { + SendCommandAttackGround(unit, newpos, flush); + } + else if (strcmp(command, "patrol") == 0) { + SendCommandPatrol(unit, newpos, flush); + } + else { + printf("Function CommandFunctionalities received an unknown command: %s\n", command); + SendCommandMove(unit, newpos, flush); + } + } /*---------------------------------------------------------------------------- -- Functions *************** *** 104,109 **** --- 166,172 ---- */ void SendCommandMove(CUnit &unit, const Vec2i &pos, int flush) { + if (!IsNetworkGame()) { CommandLog("move", &unit, flush, pos.x, pos.y, NoUnitP, NULL, -1); CommandMove(unit, pos, flush); diff -r -c stratagus_2.2.7.veryorig/src/stratagus/selection.cpp stratagus_2.2.7.patched/src/stratagus/selection.cpp *** stratagus_2.2.7.veryorig/src/stratagus/selection.cpp 2013-10-03 19:56:26.449550587 +0300 --- stratagus_2.2.7.patched/src/stratagus/selection.cpp 2013-10-11 06:08:29.565952998 +0300 *************** *** 1108,1119 **** --- 1108,1139 ---- } /** + ** Push the table SelectedUnitTable that contains the ID's of units we have selected + ** + ** @param l Lua state. + */ + static int CclGetSelection(lua_State *l) + { + LuaCheckArgs(l, 0); + lua_newtable(l); + + for (int i = 0; i < NumSelected; ++i) { + Assert(Selected[i]); + lua_pushnumber(l, i+1); /* Push the table index */ + lua_pushnumber(l, UnitNumber(*Selected[i])); /* Push the cell value */ + lua_rawset(l, -3); /* Stores the pair in the table */ + } + lua_setglobal(l, "SelectedUnitTable"); // By what name is the script going to reference our table? + } + + /** ** Register CCL features for selections. */ void SelectionCclRegister() { lua_register(Lua, "SetGroupId", CclSetGroupId); lua_register(Lua, "Selection", CclSelection); + lua_register(Lua, "GetSelection", CclGetSelection); } //@} diff -r -c stratagus_2.2.7.veryorig/src/ui/mouse.cpp stratagus_2.2.7.patched/src/ui/mouse.cpp *** stratagus_2.2.7.veryorig/src/ui/mouse.cpp 2013-10-03 19:56:26.449550587 +0300 --- stratagus_2.2.7.patched/src/ui/mouse.cpp 2013-10-11 03:02:56.489980763 +0300 *************** *** 185,191 **** return false; } ! static bool DoRightButton_Harvest(CUnit &unit, CUnit *dest, const Vec2i &pos, int flush, int &acknowledged) { const CUnitType &type = *unit.Type; --- 185,191 ---- return false; } ! static bool DoRightButton_Harvest(int selectednumber, CUnit &unit, CUnit *dest, const Vec2i &pos, int flush, int &acknowledged) { const CUnitType &type = *unit.Type; *************** *** 268,278 **** PlayUnitSound(unit, VoiceAcknowledging); acknowledged = 1; } ! SendCommandMove(unit, pos, flush); return true; } ! static void DoRightButton_Attack(CUnit &unit, CUnit *dest, const Vec2i &pos, int flush, int &acknowledged) { const CUnitType &type = *unit.Type; const int action = type.MouseAction; --- 268,278 ---- PlayUnitSound(unit, VoiceAcknowledging); acknowledged = 1; } ! CommandFunctionalities("move", selectednumber, unit, pos, flush); return true; } ! static void DoRightButton_Attack(int selectednumber, CUnit &unit, CUnit *dest, const Vec2i &pos, int flush, int &acknowledged) { const CUnitType &type = *unit.Type; const int action = type.MouseAction; *************** *** 295,301 **** if (CanTarget(&type, dest->Type)) { SendCommandAttack(unit, pos, dest, flush); } else { // No valid target ! SendCommandAttack(unit, pos, NoUnitP, flush); } } return; --- 295,301 ---- if (CanTarget(&type, dest->Type)) { SendCommandAttack(unit, pos, dest, flush); } else { // No valid target ! CommandFunctionalities("attackemptyspace", selectednumber, unit, pos, flush); } } return; *************** *** 323,329 **** // empty space if ((KeyModifiers & ModifierControl)) { if (RightButtonAttacks) { ! SendCommandMove(unit, pos, flush); if (!acknowledged) { PlayUnitSound(unit, VoiceAcknowledging); acknowledged = 1; --- 323,329 ---- // empty space if ((KeyModifiers & ModifierControl)) { if (RightButtonAttacks) { ! CommandFunctionalities("move", selectednumber, unit, pos, flush); if (!acknowledged) { PlayUnitSound(unit, VoiceAcknowledging); acknowledged = 1; *************** *** 333,339 **** PlayUnitSound(unit, VoiceAttack); acknowledged = 1; } ! SendCommandAttack(unit, pos, NoUnitP, flush); } } else { if (RightButtonAttacks) { --- 333,339 ---- PlayUnitSound(unit, VoiceAttack); acknowledged = 1; } ! CommandFunctionalities("attackemptyspace", selectednumber, unit, pos, flush); } } else { if (RightButtonAttacks) { *************** *** 341,354 **** PlayUnitSound(unit, VoiceAttack); acknowledged = 1; } ! SendCommandAttack(unit, pos, NoUnitP, flush); } else { // Note: move is correct here, right default is move if (!acknowledged) { PlayUnitSound(unit, VoiceAcknowledging); acknowledged = 1; } ! SendCommandMove(unit, pos, flush); } } // FIXME: ALT-RIGHT-CLICK, move but fight back if attacked. --- 341,354 ---- PlayUnitSound(unit, VoiceAttack); acknowledged = 1; } ! CommandFunctionalities("attackemptyspace", selectednumber, unit, pos, flush); } else { // Note: move is correct here, right default is move if (!acknowledged) { PlayUnitSound(unit, VoiceAcknowledging); acknowledged = 1; } ! CommandFunctionalities("move", selectednumber, unit, pos, flush); } } // FIXME: ALT-RIGHT-CLICK, move but fight back if attacked. *************** *** 423,429 **** return false; } ! static void DoRightButton_ForSelectedUnit(CUnit &unit, CUnit *dest, const Vec2i &pos, int &acknowledged) { // don't self targetting. if (dest == &unit) { --- 423,429 ---- return false; } ! static void DoRightButton_ForSelectedUnit(int selectednumber, CUnit &unit, CUnit *dest, const Vec2i &pos, int &acknowledged) { // don't self targetting. if (dest == &unit) { *************** *** 451,463 **** // Handle resource workers. if (action == MouseActionHarvest) { ! DoRightButton_Harvest(unit, dest, pos, flush, acknowledged); return; } // Fighters if (action == MouseActionSpellCast || action == MouseActionAttack) { ! DoRightButton_Attack(unit, dest, pos, flush, acknowledged); return; } --- 451,463 ---- // Handle resource workers. if (action == MouseActionHarvest) { ! DoRightButton_Harvest(selectednumber, unit, dest, pos, flush, acknowledged); return; } // Fighters if (action == MouseActionSpellCast || action == MouseActionAttack) { ! DoRightButton_Attack(selectednumber, unit, dest, pos, flush, acknowledged); return; } *************** *** 485,491 **** PlayUnitSound(unit, VoiceAcknowledging); acknowledged = 1; } ! SendCommandMove(unit, pos, flush); } /** --- 485,491 ---- PlayUnitSound(unit, VoiceAcknowledging); acknowledged = 1; } ! CommandFunctionalities("move", selectednumber, unit, pos, flush); } /** *************** *** 537,543 **** Assert(Selected[i]); CUnit &unit = *Selected[i]; ! DoRightButton_ForSelectedUnit(unit, dest, pos, acknowledged); } ShowOrdersCount = GameCycle + Preference.ShowOrders * CYCLES_PER_SECOND; } --- 537,543 ---- Assert(Selected[i]); CUnit &unit = *Selected[i]; ! DoRightButton_ForSelectedUnit(i, unit, dest, pos, acknowledged); } ShowOrdersCount = GameCycle + Preference.ShowOrders * CYCLES_PER_SECOND; } *************** *** 1031,1037 **** SendCommandBoard(*unit, *transporter, flush); ret = 1; } else { ! SendCommandMove(*unit, tilePos, flush); ret = 1; } } --- 1031,1037 ---- SendCommandBoard(*unit, *transporter, flush); ret = 1; } else { ! CommandFunctionalities("move", i, *unit, tilePos, flush); ret = 1; } } *************** *** 1066,1081 **** CUnit &unit = *Selected[i]; if (unit.Type->CanAttack) { ! if (!dest || (dest != &unit && CanTarget(unit.Type, dest->Type))) { ! if (dest) { ! dest->Blink = 4; ! } SendCommandAttack(unit, tilePos, dest, flush); ret = 1; } } else { if (unit.CanMove()) { ! SendCommandMove(unit, tilePos, flush); ret = 1; } } --- 1066,1083 ---- CUnit &unit = *Selected[i]; if (unit.Type->CanAttack) { ! if (!dest) { ! CommandFunctionalities("attackemptyspace", i, unit, tilePos, flush); ! ret = 1; ! } ! else if (dest != &unit && CanTarget(unit.Type, dest->Type)) { ! dest->Blink = 4; SendCommandAttack(unit, tilePos, dest, flush); ret = 1; } } else { if (unit.CanMove()) { ! CommandFunctionalities("move", i, unit, tilePos, flush); ret = 1; } } *************** *** 1096,1105 **** for (int i = 0; i < NumSelected; ++i) { CUnit &unit = *Selected[i]; if (unit.Type->CanAttack) { ! SendCommandAttackGround(unit, tilePos, flush); ret = 1; } else { ! SendCommandMove(unit, tilePos, flush); ret = 1; } } --- 1098,1107 ---- for (int i = 0; i < NumSelected; ++i) { CUnit &unit = *Selected[i]; if (unit.Type->CanAttack) { ! CommandFunctionalities("attackground", i, unit, tilePos, flush); ret = 1; } else { ! CommandFunctionalities("move", i, unit, tilePos, flush); ret = 1; } } *************** *** 1117,1123 **** for (int i = 0; i < NumSelected; ++i) { CUnit &unit = *Selected[i]; ! SendCommandPatrol(unit, tilePos, flush); } return NumSelected != 0 ? 1 : 0; } --- 1119,1125 ---- for (int i = 0; i < NumSelected; ++i) { CUnit &unit = *Selected[i]; ! CommandFunctionalities("patrol", i, unit, tilePos, flush); } return NumSelected != 0 ? 1 : 0; }