- Joined
- Nov 4, 2007
- Messages
- 337
Requirements: Dialog System, Table
Description:
This is a system that handles making alliances ingame.
The players can build alliances using the commands '-ally' and '-war'.
Actually it is a very simple thing, but I think there are more things necessary to make the ally system be a good ally system that appears well and does not fail under certain circumstances. Following you can see a list with the features that are useful in my opinion.
Features
Testing
This has been tested intensively. Everything works as it should.
So you can trust to the system.
Future plans
I will eventually add an option that makes that only whole alliances are allowed.
Example: player A and B are allied, player B and C are allied, player A and C are at war
==> The alliance is not totally complete.
The constant will force everybody in an alliance to confirm the ally.
Code
Can be found in the testmap which includes an example, too.
I am happy about suggestions and feedback :emote_grin:.
If the dialog system is not approved, Ill update the code.
Description:
This is a system that handles making alliances ingame.
The players can build alliances using the commands '-ally' and '-war'.
Actually it is a very simple thing, but I think there are more things necessary to make the ally system be a good ally system that appears well and does not fail under certain circumstances. Following you can see a list with the features that are useful in my opinion.
Features
- Players can't change initial alliances when forbidden by a constant. This means they can not declare war on initial allies.
- The messages that appear when players ally appear well (colored player names, correct english (I hope))
- A player declares automatically war on a player he allied that attacks him.
- The system supports numbers, color names and playernames in the commands.
- I think most players are going to use colors: Colors have extra support. Different names for the same color is possible (like grey and gray or lightblue and light blue)
- If a player a allies another player b there is a peace time: A period in which the player a can't declare war manually on player b
- If allying fails, the player that tried to ally gets explicit error messages
- Players have an ally limit that forbids building too big alliances
- Not all players can be in the same team. There is always struggle
- You can forbid partial alliances. If partial alliances are forbidden, a player sends an ally request to another player if he wants to ally him. If the other player confirms the request, then both players ally each other. If a player declares war on the other player, the alliance dissolves for both sides.
Testing
This has been tested intensively. Everything works as it should.
So you can trust to the system.
Future plans
I will eventually add an option that makes that only whole alliances are allowed.
Example: player A and B are allied, player B and C are allied, player A and C are at war
==> The alliance is not totally complete.
The constant will force everybody in an alliance to confirm the ally.
Code
Can be found in the testmap which includes an example, too.
JASS:
library AllianceSystem initializer Init requires DialogSystem, Table
//===========================================================================
// Information:
//==============
// Requirements: Dialog System (which requires Table)
//
// Most alliance systems suck and have some mistakes that occur when the maker
// of the system didn't think well before making it.
// I thought clearly about the things I wanted to make and that'd be good for an
// ally system before I made this;
// - You can't change initial alliances when permitted
// - A player is auto-warred if he attacks a player he did not ally that allied him.
// - Supports different ways of allying: player numbers, player names and color names
// - Supports different names for the same color (like lightblue and light blue or grey and gray)
// - If a player a allies another player b there is a peace time: Player a can't
// declare war on player b during that time.
// - If allying fails, exact information why it fails are displayed to the player who tried to ally.
// - Players have an ally-limit that permits making too big forces.
// - Not all players can be allied with each other.
//
//===========================================================================
// Implementation:
//===============
//
// 1. Download a tool called 'JassNewGen', and unpack it somewhere. You need that
// edit to use this tool. The 'JassNewGen' is used very commonly and offers other
// nice features. You can find it at:
// [url]http://www.wc3c.net/showthread.php?t=90999[/url]
//
// 2. Make a new trigger, and convert it to custom text. Insert everything
// the library contains into that trigger.
//
// 3. Download a system called 'Table' from this link:
// [url]http://www.wc3c.net/showthread.php?t=101246[/url]
// Do the same installation stuff for 'Table' as for this system.
//
// 4. Save your map and enjoy :-)
//
//===========================================================================
globals
// If this is set to false, players can't declare war on players
// they had an initial alliance with.
private constant boolean ALLOW_CHANGING_INITIAL_ALLIANCES = true
// If you do not allow partial alliance and a player a wants to ally a player b,
// player b gets a request in form of a dialog (Do you want to ally bla?).
// If player b rejects the request, he is not allied.
// When they built an alliance and one of the player declares war, the
// other player declares war automatically, too.
// This permits building partial alliances; Only full alliances are
// allowed if this is set to false.
private constant boolean ALLOW_PARTIAL_ALLIANCES = true
// Players can't have more than MAX_ALLY_COUNT allies and they can't
// ally everybody. So if there are just two players left, they can't ally
// each other.
private constant integer MAX_ALLY_COUNT = 2
// If a player allies another player he has to wait PEACE_TIME seconds in
// order to declare war.
private constant real PEACE_TIME = 120.
endglobals
globals
private trigger AllyTrigger = CreateTrigger()
private trigger WarTrigger = CreateTrigger()
private trigger AttackTrigger = CreateTrigger()
private trigger LeaveTrigger = CreateTrigger()
private timer array Wartimer
private boolean array DeclareWarOnExpire
private boolean array InitialAlly
private integer array AllyCount
private string array ColorString
private integer Playercount = 0
private HandleTable TimerData
private constant integer SIZE = 12
endglobals
// I use colorcodes of a blizz map.
// The ARGB Playercolors totally suck. Vexorian should update them,
// they don't look natural. Teal e.g looks like green.
private function ColorPlayer takes player p returns string
return ColorString[GetPlayerId(p)]+GetPlayerName(p)+"|r"
endfunction
private struct UserMethods
// These both methods are used if ALLOW_PARTIAL_ALLIANCES is true
static method onAlly takes player allying, player allied returns nothing
call DisplayTextToPlayer(GetLocalPlayer(),0,0,ColorPlayer(allying)+" has allied "+ColorPlayer(allied))
endmethod
static method onWar takes player warring, player warred returns nothing
call DisplayTextToPlayer(GetLocalPlayer(),0,0,ColorPlayer(warring)+" has declared war on "+ColorPlayer(warred))
endmethod
// These both methods are used if ALLOW_PARTIAL_ALLIANCES is false
static method onAllyEx takes player allying, player allied returns nothing
call DisplayTextToPlayer(GetLocalPlayer(),0,0,ColorPlayer(allying)+" and "+ColorPlayer(allied)+" have built an alliance.")
endmethod
static method onWarEx takes player warring, player warred returns nothing
call DisplayTextToPlayer(GetLocalPlayer(),0,0,"The alliance between "+ColorPlayer(warring)+" and "+ColorPlayer(warred)+" has dissolved.")
endmethod
endstruct
// Makes a single index from two indexes, where a has a certain size.
// We need this to store Data that are relevant between two players.
// We can turn the index back to the two integers the index was created of.
private function Index2D takes integer a, integer b returns integer
return a + b*SIZE
endfunction
private function IndexA takes integer Matrize returns integer
return ModuloInteger(Matrize,SIZE)
endfunction
private function IndexB takes integer Matrize returns integer
return (Matrize/SIZE)
endfunction
function GetLabelPlayer takes string label returns player
local integer i = 0
// integers
if S2I(label) != 0 then
if S2I(label)-1 < 12 and S2I(label)-1 > 0 then
return Player(S2I(label)-1)
endif
endif
set label = StringCase(label,false)
// Colors
if label == "red" then
return Player(0)
elseif label == "blue" then
return Player(1)
elseif label == "teal" then
return Player(2)
elseif label == "purple" then
return Player(3)
elseif label == "yellow" then
return Player(4)
elseif label == "orange" then
return Player(5)
elseif label == "green" then
return Player(6)
elseif label == "pink" then
return Player(7)
elseif label == "grey" then
return Player(8)
elseif label == "gray" then
return Player(8)
elseif label == "lightblue" then
return Player(9)
elseif label == "light blue" then
return Player(9)
elseif label == "light-blue" then
return Player(9)
elseif label == "darkgreen" then
return Player(10)
elseif label == "dark green" then
return Player(10)
elseif label == "dark-green" then
return Player(10)
elseif label == "brown" then
return Player(11)
endif
//Playernames
loop
exitwhen i > 11
if label == StringCase(GetPlayerName(Player(i)),false) then
return Player(i)
endif
set i = i + 1
endloop
return null
endfunction
private function onWartimerExpire takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer index = TimerData[t]
if DeclareWarOnExpire[index] then
// Ahahaha
// Mwaahahaha I'm a genius. Really.
call SetAlliance.execute(Player(IndexA(index)),Player(IndexB(index)),false)
endif
endfunction
function interface SetAllianceInterface takes player from, player towards, boolean ally returns nothing
// This is a very important function. It sets alliance conditions, starts the wartimers,
// handles the allycount and the usermethods.
function SetAlliance takes player from, player towards, boolean ally returns nothing
local integer index = Index2D(GetPlayerId(from),GetPlayerId(towards))
if ally then
call SetPlayerAlliance(from,towards,ALLIANCE_PASSIVE,true)
call SetPlayerAlliance(from,towards,ALLIANCE_SHARED_VISION,true)
set AllyCount[GetPlayerId(from)] = AllyCount[GetPlayerId(from)] + 1
if ALLOW_PARTIAL_ALLIANCES then
call UserMethods.onAlly(from,towards)
if Wartimer[index] == null then
set Wartimer[index] = CreateTimer()
set TimerData[Wartimer[index]] = index
endif
set DeclareWarOnExpire[index] = false
// Restarting the timer = Refreshing period.
call TimerStart(Wartimer[index],PEACE_TIME,false,function onWartimerExpire)
elseif GetPlayerAlliance(towards,from,ALLIANCE_PASSIVE) == true then
call UserMethods.onAllyEx(from,towards)
endif
else
call SetPlayerAlliance(from,towards,ALLIANCE_PASSIVE,false)
call SetPlayerAlliance(from,towards,ALLIANCE_SHARED_VISION,false)
set AllyCount[GetPlayerId(from)] = AllyCount[GetPlayerId(from)] - 1
if ALLOW_PARTIAL_ALLIANCES then
call UserMethods.onWar(from,towards)
else
set AllyCount[GetPlayerId(towards)] = AllyCount[GetPlayerId(towards)] - 1
call SetPlayerAlliance(towards,from,ALLIANCE_PASSIVE,false)
call SetPlayerAlliance(towards,from,ALLIANCE_SHARED_VISION,false)
call UserMethods.onWarEx(from,towards)
endif
endif
endfunction
function ConfirmAlly takes integer value, Dialog confirm returns nothing
local player p2 = Player(confirm.GetData())
if value == 1 then
call SetAlliance(GetClickingPlayer(),p2,true)
call SetAlliance(p2,GetClickingPlayer(),true)
else
call DisplayTextToPlayer(p2,0,0,ColorPlayer(GetClickingPlayer())+" has refused your request.")
endif
endfunction
// OnAlly and onWar are a mess, I know, but all the safety things are neccessary and useful
private function OnAlly takes nothing returns nothing
local player ally = GetLabelPlayer(SubString(GetEventPlayerChatString(),6,StringLength(GetEventPlayerChatString())))
local integer index = 0
local Dialog D
set index = Index2D(GetPlayerId(GetTriggerPlayer()),GetPlayerId(ally))
if ally == null then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r This player does not exist.")
return
elseif AllyCount[GetPlayerId(GetTriggerPlayer())] == MAX_ALLY_COUNT then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r You can't have more than "+I2S(MAX_ALLY_COUNT)+" allies.")
return
elseif ally == GetTriggerPlayer() then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r You can't ally yourself.")
return
// May seem strange that I use -2.
// We have to subtract one because the allying player is not valid.
// And another one because We are GOING to have everybody-allied who if it was -1
// 4 Players. A player may have 2 allies. If red has three allies, he allied everybody.
elseif AllyCount[GetPlayerId(GetTriggerPlayer())] >= Playercount-2 then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r You can't ally everybody.")
return
elseif GetPlayerSlotState(ally) != PLAYER_SLOT_STATE_PLAYING then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r "+ColorPlayer(ally)+" is not playing.")
return
elseif GetPlayerController(ally) != MAP_CONTROL_USER then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r You can't ally Computer Players.")
return
elseif GetPlayerAlliance(GetTriggerPlayer(),ally,ALLIANCE_PASSIVE) == true then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r You already allied "+ColorPlayer(ally)+".")
return
endif
if ALLOW_PARTIAL_ALLIANCES then
call SetAlliance(GetTriggerPlayer(),ally,true)
else
set D = Dialog.create("Would you like to ally "+ColorPlayer(GetTriggerPlayer())+"?",ConfirmAlly,0.)
call D.AddOption("Yes",1)
call D.AddOption("No",2)
call D.SetData(GetPlayerId(ally))
call D.ShowFor(ally)
endif
endfunction
private function OnWar takes nothing returns nothing
local player ally
local integer index
if SubString(GetEventPlayerChatString(),0,4) == "-war" then
set ally = GetLabelPlayer(SubString(GetEventPlayerChatString(),5,StringLength(GetEventPlayerChatString())))
else
set ally = GetLabelPlayer(SubString(GetEventPlayerChatString(),8,StringLength(GetEventPlayerChatString())))
endif
set index = Index2D(GetPlayerId(GetTriggerPlayer()),GetPlayerId(ally))
if ally == null then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r This player does not exist.")
return
elseif ally == GetTriggerPlayer() then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r You can't declare war on yourself.")
return
elseif GetPlayerSlotState(ally) != PLAYER_SLOT_STATE_PLAYING then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r "+ColorPlayer(ally)+" is not playing.")
return
elseif InitialAlly[index] == true then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r Changing initial alliances is not allowed.")
return
elseif GetPlayerAlliance(GetTriggerPlayer(),ally,ALLIANCE_PASSIVE) == false then
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r You are not an ally of "+ColorPlayer(ally)+"s.")
return
elseif TimerGetRemaining(Wartimer[index]) > 0. then
set DeclareWarOnExpire[index] = true
call DisplayTextToPlayer(GetTriggerPlayer(),0,0,"|cffff0000Error:|r You can't declare war on "+ColorPlayer(ally)+" yet. War will be automatically declared in "+I2S(R2I(TimerGetRemaining(Wartimer[index])))+" seconds.")
return
endif
call SetAlliance(GetTriggerPlayer(),ally,false)
endfunction
private function OnAttack takes nothing returns nothing
local integer index
local player p1 = GetOwningPlayer(GetTriggerUnit())
local player p2 = GetOwningPlayer(GetAttacker())
if GetPlayerAlliance(p1,p2,ALLIANCE_PASSIVE) == true then
if GetPlayerAlliance(p2,p1,ALLIANCE_PASSIVE) == false then
set index = Index2D(GetPlayerId(p1),GetPlayerId(p2))
set DeclareWarOnExpire[index] = false
// Refresh the timeout.
call PauseTimer(Wartimer[index])
call SetAlliance(p1,p2,false)
endif
endif
endfunction
// This prevents that everybody is in a team.
// It's a slow function but it's fired rarely.
// Speed bonuses are neglegible
private function OnLeave takes nothing returns nothing
local integer i = 0
local integer i2
local integer allyCount
local integer array allies
local integer random
set Playercount = Playercount - 1
loop
exitwhen i > 11
// Player i allied everybody. BÖP!
if GetPlayerAlliance(Player(i),GetTriggerPlayer(),ALLIANCE_PASSIVE) == true then
set AllyCount[i] = AllyCount[i] - 1
endif
if AllyCount[i] == Playercount-1 then
set i2 = 0
set allyCount = 0
loop
exitwhen i2 > 11
if GetPlayerAlliance(Player(i),Player(i2),ALLIANCE_PASSIVE) == true then
// We store the allies to pick a random ally and make him be an enemy
set allyCount = allyCount + 1
set allies[allyCount] = i2
endif
set i2 = i2 + 1
endloop
set random = allies[GetRandomInt(1,allyCount)]
call DisplayTextToPlayer(GetLocalPlayer(),0,0,ColorPlayer(Player(i))+" declares automatically war on "+ColorPlayer(Player(random))+", because "+ColorPlayer(Player(i))+" allied everybody.")
call SetAlliance(Player(i),Player(random),false)
endif
set i = i + 1
endloop
endfunction
private function Init takes nothing returns nothing
local integer i = 0
local integer i2
local integer index
loop
exitwhen i > 11
if GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(i)) == MAP_CONTROL_USER then
set Playercount = Playercount + 1
call TriggerRegisterPlayerChatEvent( AllyTrigger , Player(i), "-ally" , false )
call TriggerRegisterPlayerChatEvent( WarTrigger , Player(i), "-unally", false )
call TriggerRegisterPlayerChatEvent( WarTrigger , Player(i), "-war" , false )
call TriggerRegisterPlayerUnitEvent( AttackTrigger, Player(i), EVENT_PLAYER_UNIT_ATTACKED, null)
call TriggerRegisterPlayerEvent ( LeaveTrigger , Player(i), EVENT_PLAYER_LEAVE)
call TriggerRegisterPlayerEvent ( LeaveTrigger , Player(i), EVENT_PLAYER_DEFEAT)
set i2 = 0
loop
exitwhen i2 > 11
if i2 != i then
if IsPlayerAlly(Player(i),Player(i2)) then
set InitialAlly[Index2D(i,i2)] = true
set Allies[i] = Allies[i] + 1
endif
endif
set i2 = i2 + 1
endloop
endif
set i = i + 1
endloop
call TriggerAddAction(AllyTrigger,function OnAlly)
call TriggerAddAction(WarTrigger,function OnWar)
call TriggerAddAction(LeaveTrigger,function OnLeave)
if ALLOW_PARTIAL_ALLIANCES then
call TriggerAddAction(AttackTrigger,function OnAttack)
else
call DestroyTrigger(AttackTrigger)
endif
set TimerData = Table.create()
// PlayerColors
set ColorString[0] = "|cffff0000" //red
set ColorString[1] = "|cff0000ff" //blue
set ColorString[2] = "|cff00f5ff" //Teal
set ColorString[3] = "|cff551A8B" //Purple
set ColorString[4] = "|cffffff00" //Yellow
set ColorString[5] = "|cffEE9A00" //Orange
set ColorString[6] = "|cff00CD00" //Green
set ColorString[7] = "|cffFF69B4" //Pink
set ColorString[8] = "|cffC0C0C0" //Gray
set ColorString[9] = "|cffB0E2FF" //Light Blue
set ColorString[10] = "|cff006400" //Dark Green
set ColorString[11] = "|cff8B4513" //Brown
endfunction
endscope
I am happy about suggestions and feedback :emote_grin:.
If the dialog system is not approved, Ill update the code.
Last edited: