| 08-07-2009, 09:12 AM | #1 |
Okay, so there's already a thread vaguely about this floating around somewhere, but I hoped to raise awareness to this by making a new thread. Basically, I wrote a system that should in theory switch two players in-game. If I was player 1 and I switched with player 2, all of his units would become mine, my name would change to his, my color would change too, and so would my resources, alliances, etc.. Effectively as if Player 2 and I had stood up and switched computers. Cool, right? Well it's a little complicated. It involves wrapping GetPlayerId() and Player() so they work with variable PlayerIds, and then there's a whole bunch of stupid stuff that might have to be dealt with. However, here's what I have: //Old Code removed, see later post... The problem is that I'm stuck because I can't get 2 people to test this with online, so I'm not sure if any of it actually works. My hope is that if I put this up here, someone can test it for me to see if I went wrong anywhere along the way, or if it actually works as intended. The other thing is that there are a few PlayerStates that I'm unsure about. In the script there are 6 lines marked with //?, because I'm not actually sure if they're necessary. I have no idea what PLAYER_STATE_GAME_RESULT means/is for, and I think the food and gold/lumber rates should be updated automatically when the units are switched over, right? Anyway, it would be nice if someone could test this or give some input on those few lines. |
| 08-07-2009, 01:25 PM | #2 |
do you use faked ids, or do you really change the players? Can you make something to switch with an empty position? (I'm to stupid for that, tried 2 times, 4 fails) If it works without fakes ids then yours is better than mine |
| 08-07-2009, 02:42 PM | #3 | |
Quote:
*cough* |
| 08-07-2009, 05:18 PM | #4 |
If this works, it could make for some trippy gameplay stuff. |
| 08-07-2009, 05:23 PM | #5 |
Even if it works it can fail in many cases, if you use variable instead of Player() for example. But i guess explain it in the documentation is enough fine. |
| 08-07-2009, 07:05 PM | #6 |
I didn't read the whole script, but one thing that popped into my mind was: What about researches? I thought it was impossible to "unresearch" something. |
| 08-09-2009, 07:50 AM | #7 |
Yeah, I didn't think about techtree research or anything... Although I suppose that if you also triggered your upgrades too it would work. This calls for a companion library! And besides, upgrades need a decent library anyway. Tot, the only way to do this is is with "faked" Ids; there are two imortant wrappers in this system: GetPlayerIdEx() and PlayerEx(). Alevice, yeah I forgot about wc3cr whilst writing this... |
| 08-10-2009, 08:34 AM | #8 |
Alright, so I made a fair amount of changes. I added comments, fixed anything weird that would have happened with SetPlayerAbilityAvailable(), and I hope to get a standalone Upgrade simulation library working. Here's the current version: JASS:library PlayerSwitch initializer Init uses BoolexprUtils, LinkedList //================================================================ // PlayerSwitch script by Pyrogasm - Version 1.1 - August 9, 2009 //================================================================ // // The function of this script is to switch two players in-game. // Imagine that you and Player X are sitting at two different computers; // you then both stand up and walk to the other's computer and sit down. // // That is effectively what this script does, except you can switch // with any player in the game (not just your buddy next to you, and // you don't have to move :) // // There are a few side-effects: // - You can no longer use Player() GetPlayerId() and // SetPlayerAbilityAvailable(); instead, each has its own // "...Ex()" version that you can use instead with no loss in // functionality. // // - Upgrades are currently broken because you can't un-upgrade something // This really shouldn't be that much of a problem for most mapmakers; // however I am planning on writing a companion library to simulate // upgrades using units so that more functionality may exist. // // Should such a library be written, I will appropriately update this // library. // // - I'm not entirely sure if this all works properly, or even what some // of it does (See the lines marked with "//?"), and there may be // things other than upgrades I haven't thought of yet that are non- // transferrable. If you think of something, bring it to my attention // at [url]http://www.Wc3c.net/member.php?u=747681[/url] (send me a PM) // // Usage: // - Use PlayerEx(), GetPlayerIdEx(), and SetPlayerAbilityAvailableEx() // instead of their normal counterparts // // - Don't store the owner of a unit in a variable for too long // // - Don't use upgrades you don't want transferred (yet) // // - Simply call SwitchPlayers(Player1, Player2) and they will switch // // - Chill, serve, and enjoy //================================================================ // // Thanks to: // - Vexorian, for JASSHelper, and all that jazz // - Mindworx, PitzerMike, Zoxc, Pipedream, and all who have developed // the JASSNewGen Pack at any point // - Ammorth, for his LinkedList script // - Tot, for the inspiration to write this // - Those who have given me critique regarding this // //================================================================ // There is no configuration, in case you were wondering. //================================================================ globals private keyword PlayerInfo //Some relevant globals private PlayerInfo array PIs private player Switch private integer Team //These are used during the switch private playercolor Color private string Name private integer Id private boolean Alliance private integer State private group G1 //Because creating groups all the time is bad private group G2 endglobals private struct PlayerInfo //I could have made it extend an array, but I would have lost the cool [] operator syntax player P //This struct stores the relevant data for every player integer Id //Player Ids are not static, so they must be saved List AList //This list stores which abilities are currently not avaiable to each player static method operator [] takes player P returns PlayerInfo return PIs[GetPlayerId(P)] endmethod static method create takes player P, integer J returns PlayerInfo local PlayerInfo PI = PlayerInfo.allocate() set PI.Id = J set PI.P = P set PI.AList = List.create() return PI endmethod method onDestroy takes nothing returns nothing //When would this ever be called...? call .AList.destroy() endmethod endstruct //Debug functions and hooks to warn against the use of 'deprecated' functions debug function GetPlayerIdError takes nothing returns nothing debug call BJDebugMsg("|cffff0000Warning: Used GetPlayerId() instead of GetPlayerIdEx()!") debug endfunction debug function PlayerError takes nothing returns nothing debug call BJDebugMsg("|cffff0000Warning: Used Player() instead of PlayerEx()!") debug endfunction debug function AvailableError takes nothing returns nothing debug call BJDebugMsg("|cffff0000Warning: Used SetPlayerAbilityAvailable() instead of SetPlayerAbilityAvailableEx()!") debug endfunction debug hook GetPlayerId GetPlayerIdError debug hook Player PlayerError debug hook SetPlayerAbilityAvailable AvailableError function SetPlayerAbilityAvailableEx takes player whichPlayer, integer whichAbil, boolean flag returns nothing local PlayerInfo PI = PlayerInfo[whichPlayer] //Get the relevant PlayerInfo local Link L if not flag then //If the ability is to be unavailable call Link.create(PI.AList, whichAbil) //Add it to the list of unavailable abilities call SetPlayerAbilityAvailable(whichPlayer, whichAbil, false) //And actually make it unavailable else set L = PI.AList.search(whichAbil) //Make sure it is currently disabled if L != 0 then //If it is, then call SetPlayerAbilityAvailable(whichPlayer, whichAbil, true) //Enable it call L.destroy() //Remove the ability from the list of disabled abilities endif endif endfunction function GetPlayerIdEx takes player whichPlayer returns integer local PlayerInfo PI = PlayerInfo[whichPlayer] //I couldn't do return PlayerInfo[whichPlayer].Id, for some godforsaken reason return PI.Id endfunction function PlayerEx takes integer whichId returns player local integer J = 0 local PlayerInfo PI loop set PI = PIs[J] //Iterate through all the PlayerIds if PI.Id == whichId then //If the Ids match then return the player return PI.P endif set J = J+1 exitwhen J >= 12 endloop return null endfunction private function SwitchUnits takes nothing returns nothing call SetUnitOwner(GetEnumUnit(), Switch, true) //Used in SwitchPlayers to change unit ownership endfunction //Textmacroes make our lives easier and code much cleaner! //! textmacro AllianceSet takes ALLIANCE set Alliance = GetPlayerAlliance(P1, PI1.P, $ALLIANCE$) call SetPlayerAlliance(P1, PI1.P, $ALLIANCE$, GetPlayerAlliance(P2, PI1.P, $ALLIANCE$)) call SetPlayerAlliance(P2, PI1.P, $ALLIANCE$, Alliance) //! endtextmacro //! textmacro StateSet takes STATE set State = GetPlayerState(P1, $STATE$) call SetPlayerState(P1, $STATE$, GetPlayerState(P2, $STATE$)) call SetPlayerState(P2, $STATE$, State) //! endtextmacro function SwitchPlayers takes player P1, player P2 returns boolean local integer J = 0 local PlayerInfo PI1 local PlayerInfo PI2 local Link L local List SwapL if P1 == null or P2 == null then //Why would you switch with a null player? return false endif loop set PI1 = PIs[J] //Loop through all players and then set the appropriate alliance properties for each of the switching players //! runtextmacro AllianceSet("ALLIANCE_PASSIVE") //! runtextmacro AllianceSet("ALLIANCE_HELP_REQUEST") //! runtextmacro AllianceSet("ALLIANCE_HELP_RESPONSE") //! runtextmacro AllianceSet("ALLIANCE_SHARED_XP") //! runtextmacro AllianceSet("ALLIANCE_SHARED_VISION") //! runtextmacro AllianceSet("ALLIANCE_SHARED_CONTROL") //! runtextmacro AllianceSet("ALLIANCE_SHARED_ADVANCED_CONTROL") //! runtextmacro AllianceSet("ALLIANCE_RESCUABLE") //! runtextmacro AllianceSet("ALLIANCE_SHARED_VISION_FORCED") set J = J+1 exitwhen J >= 16 endloop //This is where the playerstates are set, but I'm not really sure what some of them do //Or even if they need to be set //? //! runtextmacro StateSet("PLAYER_STATE_GAME_RESULT") //! runtextmacro StateSet("PLAYER_STATE_RESOURCE_GOLD") //! runtextmacro StateSet("PLAYER_STATE_RESOURCE_LUMBER") //! runtextmacro StateSet("PLAYER_STATE_RESOURCE_HERO_TOKENS") //? //! runtextmacro StateSet("PLAYER_STATE_RESOURCE_FOOD_CAP") //? //! runtextmacro StateSet("PLAYER_STATE_RESOURCE_FOOD_USED") //? //! runtextmacro StateSet("PLAYER_STATE_FOOD_CAP_CEILING") //! runtextmacro StateSet("PLAYER_STATE_GIVES_BOUNTY") //! runtextmacro StateSet("PLAYER_STATE_ALLIED_VICTORY") //! runtextmacro StateSet("PLAYER_STATE_PLACED") //! runtextmacro StateSet("PLAYER_STATE_OBSERVER_ON_DEATH") //! runtextmacro StateSet("PLAYER_STATE_UNFOLLOWABLE") //? //! runtextmacro StateSet("PLAYER_STATE_GOLD_UPKEEP_RATE") //? //! runtextmacro StateSet("PLAYER_STATE_LUMBER_UPKEEP_RATE") //! runtextmacro StateSet("PLAYER_STATE_GOLD_GATHERED") //! runtextmacro StateSet("PLAYER_STATE_LUMBER_GATHERED") //! runtextmacro StateSet("PLAYER_STATE_NO_CREEP_SLEEP") set PI1 = PlayerInfo[P1] set PI2 = PlayerInfo[P2] set Team = GetPlayerTeam(P1) //These need to be stored so they can be switched later set Color = GetPlayerColor(P1) set Name = GetPlayerName(P1) set Id = PI1.Id call SetPlayerTeam(P1, GetPlayerTeam(P2)) call SetPlayerColor(P1, GetPlayerColor(P2)) call SetPlayerName(P1, GetPlayerName(P2)) set PI1.Id = PI2.Id call SetPlayerTeam(P2, Team) //See? call SetPlayerColor(P2, Color) call SetPlayerName(P2, Name) set PI2.Id = Id //Now abilities need to be enabled/disabled properly for both players so nothing stupid happens //It's just a simple loop through the list ending when they both end set L = PI1.AList.first loop exitwhen L == 0 call SetPlayerAbilityAvailable(P2, L.data, false) //Simple switching call SetPlayerAbilityAvailable(P1, L.data, true) set L = L.next endloop set L = PI2.AList.first loop exitwhen L == 0 call SetPlayerAbilityAvailable(P1, L.data, false) call SetPlayerAbilityAvailable(P2, L.data, true) set L = L.next endloop set SwapL = PI1.AList //I could clone the lists or something stupid, but instead I'll just switch them set PI1.AList = PI2.AList set PI2.AList = SwapL call GroupEnumUnitsOfPlayer(G1, P1, BOOLEXPR_TRUE) //"OfPlayer" loops get locust units and all that jazz too call GroupEnumUnitsOfPlayer(G2, P2, BOOLEXPR_TRUE) set Switch = P2 call ForGroup(G1, function SwitchUnits) //Unit switching for both players set Switch = P1 //We need a second group so all the units don't end up in the control of the second player call ForGroup(G2, function SwitchUnits) call GroupClear(G1) call GroupClear(G2) return true endfunction private function Init takes nothing returns nothing local integer J = 0 loop set PIs[J] = PlayerInfo.create(Player(J), J) //Create all of the necessary PlayerInfos set J = J+1 exitwhen J >= 12 endloop set G1 = CreateGroup() //Just initialization setting set G2 = CreateGroup() endfunction endlibrary And I still am looking for someone to shed light on the //? lines.... |
| 08-10-2009, 01:04 PM | #9 | |
Quote:
why not gating upgrades with negative upgrades eg. upgrade +a increases AS by 1 upgrade -a increases AS by -1 Only prob if upgrades can't be created via triggers: userr has to define the -upgrades self (could be quite bad (at least you're an upgrade-fanatic like me)) |
| 08-13-2009, 07:29 PM | #10 |
I think you doesn't need to set PLAYER_STATE_GOLD_UPKEEP_RATE , PLAYER_STATE_LUMBER_UPKEEP_RATE, coz when you will switch the owners of units it would be done naturally, and anyway i think theses constant can only be used to read the value not to set them. (need to test it though) PLAYER_STATE_GAME_RESULT can be setted if you can add some negative score in some way, however i dunno if it's possible. |
