From 87731c96505c4502e932a2e205a1f886700ae124 Mon Sep 17 00:00:00 2001 From: MaxED Date: Mon, 16 Jul 2012 09:45:21 +0000 Subject: [PATCH] GZDoom Builder 1.11a: Things can now be cut, copied and pasted in Visual modes. Sector geometry was not updated properly after Undo/Redo in GZDoom Visual mode. Fixed an error when user selects multiple things and attempts to view their properties in Doom map format. Added Decorate and Modeldef syntax hilighting, autocompletion and item recognition to Script editor. Script Editor can now autodetect several script types when you open them (currently ACS, Decorate and Modeldef scripts are recognized). Tag Explorer: fixed a bug when Tag Explorer update can block keyboard keys release detection logic. --- Build/Scripting/GZDoom_MODELDEF.cfg | 35 ++ Build/Scripting/ZDoom_DECORATE.cfg | 340 +++++++++++++++++- Help/gz_features.html | 2 +- Source/Core/Builder.csproj | 3 + Source/Core/Builder.sln | 12 + Source/Core/Config/ArgumentInfo.cs | 2 + Source/Core/Controls/ScriptDocumentTab.cs | 122 +++---- Source/Core/Controls/ScriptEditorPanel.cs | 15 +- Source/Core/Controls/ScriptFileDocumentTab.cs | 10 +- Source/Core/Controls/ScriptLumpDocumentTab.cs | 6 +- Source/Core/Data/DataManager.cs | 2 +- Source/Core/GZBuilder/Data/ScriptItem.cs | 13 +- Source/Core/GZBuilder/Data/ScriptType.cs | 18 + Source/Core/GZBuilder/Data/ThingCopyData.cs | 57 +++ .../GZBuilder/GZDoom/ScriptTypeParserSE.cs | 79 ++++ Source/Core/VisualModes/VisualMode.cs | 19 +- Source/Core/Windows/ErrorsForm.Designer.cs | 1 + Source/Core/ZDoom/ActorStructure.cs | 14 +- .../VisualModes/BaseVisualMode.cs | 182 +++++++--- .../VisualModes/BaseVisualThing.cs | 1 - .../VisualModes/BaseVisualMode.cs | 216 ++++++++--- .../VisualModes/BaseVisualThing.cs | 1 - Source/Plugins/TagExplorer/BuilderPlug.cs | 8 +- .../Controls/TagExplorer.Designer.cs | 7 + .../TagExplorer/Controls/TagExplorer.cs | 27 +- .../TagExplorer/Controls/TagExplorer.resx | 190 +++++----- 26 files changed, 1062 insertions(+), 320 deletions(-) create mode 100644 Build/Scripting/GZDoom_MODELDEF.cfg create mode 100644 Source/Core/GZBuilder/Data/ScriptType.cs create mode 100644 Source/Core/GZBuilder/Data/ThingCopyData.cs create mode 100644 Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs diff --git a/Build/Scripting/GZDoom_MODELDEF.cfg b/Build/Scripting/GZDoom_MODELDEF.cfg new file mode 100644 index 00000000..b98e855d --- /dev/null +++ b/Build/Scripting/GZDoom_MODELDEF.cfg @@ -0,0 +1,35 @@ +/*******************************************************************\ + GZDoom Builder Script highlighting definitions for MODELDEF +\*******************************************************************/ + +// Editor settings +description = "GZDoom MODELDEF"; +codepage = 0; +extensions = "mdd"; +casesensitive = false; +insertcase = 1; // 0=Normal, 1=Lowercase, 2=Uppercase +lexer = 35; // CPP-style, case-insensitive +keywordhelp = "http://zdoom.org/wiki/MODELDEF"; + +keywords +{ + Path = "Path <\"path\">"; + Model = "Model <\"model file\">"; + Skin = "Skin <\"skin file\">"; + Scale = "Scale "; + Frame = "Frame <\"frame name\">"; + FrameIndex = "FrameIndex "; + Rotation-Speed = "Rotation-Speed "; + Rotation-Vector = "Rotation-Vector "; + Rotation-Center = "Rotation-Center "; + ZOffset = "ZOffset "; +} + +constants +{ + PITCHFROMMOMENTUM; + IGNORETRANSLATION; + INTERPOLATEDOUBLEDFRAMES; + ROTATING; + NOINTERPOLATION; +} \ No newline at end of file diff --git a/Build/Scripting/ZDoom_DECORATE.cfg b/Build/Scripting/ZDoom_DECORATE.cfg index 1a74e3a5..ce8c1458 100644 --- a/Build/Scripting/ZDoom_DECORATE.cfg +++ b/Build/Scripting/ZDoom_DECORATE.cfg @@ -1,5 +1,5 @@ /*******************************************************************\ - GZDoom Builder Script highlighting definitions for DECORATE + GZDoom Builder Script highlighting definitions for DECORATE \*******************************************************************/ // Editor settings @@ -7,29 +7,32 @@ description = "ZDoom DECORATE"; codepage = 0; extensions = "txt"; casesensitive = false; -insertcase = 1; // 0=Normal, 1=Lowercase, 2=Uppercase -lexer = 35; // CPP-style, case-insensitive +insertcase = 0; // 0=Normal, 1=Lowercase, 2=Uppercase +lexer = 35; +functionopen = "("; +functionclose = ")"; +argumentdelimiter = ","; terminator = ";"; keywordhelp = "http://www.zdoom.org/wiki/index.php?title=%K"; keywords { - #Include = "#Include"; + #Include = "#Include"; //Monster AI A_AlertMonsters = "A_AlertMonsters(float maxrange)"; - A_Burst = "A_Burst(string classname)"; - A_CentaurDefend = "A_CentaurDefend"; - A_Chase = "A_Chase[(str \"MeleeState\"[, str \"RangedState\"[, int Flags]])]"; - A_ClearLastHeard = "A_ClearLastHeard"; - A_ClearSoundTarget = "A_ClearSoundTarget"; - A_ClearTarget = "A_ClearTarget"; - A_DamageChildren = "A_DamageChildren(int amount, [string type])"; - A_DamageMaster = "A_DamageMaster(int amount, [string type])"; - A_DamageSiblings = "A_DamageSiblings(int amount, [string type])"; - A_Die = "A_Die[(str DamageType)]"; - A_FaceTarget = "A_FaceTarget[(float angle)[, float pitch]]"; - A_FaceMaster = "A_FaceMaster[(float angle)[, float pitch]]"; - A_KillChildren = "A_KillChildren(string damagetype)"; + A_Burst = "A_Burst(string classname)"; + A_CentaurDefend = "A_CentaurDefend"; + A_Chase = "A_Chase[(str \"MeleeState\"[, str \"RangedState\"[, int Flags]])]"; + A_ClearLastHeard = "A_ClearLastHeard"; + A_ClearSoundTarget = "A_ClearSoundTarget"; + A_ClearTarget = "A_ClearTarget"; + A_DamageChildren = "A_DamageChildren(int amount, [string type])"; + A_DamageMaster = "A_DamageMaster(int amount, [string type])"; + A_DamageSiblings = "A_DamageSiblings(int amount, [string type])"; + A_Die = "A_Die[(str DamageType)]"; + A_FaceTarget = "A_FaceTarget[(float angle)[, float pitch]]"; + A_FaceMaster = "A_FaceMaster[(float angle)[, float pitch]]"; + A_KillChildren = "A_KillChildren(string damagetype)"; A_KillMaster = "A_KillMaster(string damagetype)"; A_KillSiblings = "A_KillSiblings(string damagetype)"; A_Look2 = "A_Look2"; @@ -44,10 +47,313 @@ keywords A_Teleport = "A_Teleport[(string teleportstate[, string targettype[, string fogtype[, int flags[, float mindist[, float maxdist]]]]])]"; A_VileChase = "A_VileChase"; A_Wander = "A_Wander"; +//Generic monster attacks + A_CustomMissile = "A_CustomMissile(string missiletype[, float spawnheight[, int spawnofs_horiz[, angle angle[, int aimflags[, angle pitch]]]]])"; + A_CustomBulletAttack = "A_CustomBulletAttack(float horz_spread, float vert_spread, int numbullets, int damageperbullet, string pufftype [, float range [, int flags]])"; + A_CustomRailgun = "A_CustomRailgun(int damage [, int offset [, color ringcolor [, color corecolor [, int flags [, bool aim [, float maxdiff [, str pufftype[, float spread_xy[, float spread_z [, fixed range [, int duration [, float sparsity [, float driftspeed [, str spawnclass]]]]]]]]]]]]]])"; + A_CustomMeleeAttack = "A_CustomMeleeAttack(int damage[, str meleesound[, str misssound[, str damagetype[, bool bleed]]]])"; + A_CustomComboAttack = "A_CustomComboAttack(string missiletype, float spawnheight, int damage, string meleesound, string damagetype, bool bleed)"; + A_MeleeAttack = "A_MeleeAttack"; + A_MissileAttack = "A_BasicAttack(int meleedamage, string meleesound, string missiletype, float missileheight)"; + A_MonsterRefire = "A_MonsterRefire(int chancecontinue, str \"abortstate\") "; + A_ComboAttack = "A_ComboAttack"; + A_BasicAttack = "A_BasicAttack(int meleedamage, string meleesound, string missiletype, float missileheight)"; + A_BulletAttack = "A_BulletAttack"; + A_MonsterRail = "A_MonsterRail"; + A_Explode = "A_Explode[(int explosiondamage [, int explosionradius [, bool hurtshooter [, bool alert[, int fulldamageradius[, int nails[, int naildamage]]]]]])]"; + A_RadiusThrust = "A_RadiusThrust(int force, int distance[, int flags[, int fullthrustdistance]])"; + A_Detonate = "A_Detonate"; + A_ThrowGrenade = "A_ThrowGrenade(string spawntype [float spawnheight [, float throwspeed_horz [, float throwspeed_vert [, bool useammo]]]])"; +//Freeze death functions + A_FreezeDeath = "A_FreezeDeath"; + A_GenericFreezeDeath = "A_GenericFreezeDeath"; + A_FreezeDeathChunks = "A_FreezeDeathChunks"; + A_IceGuyDie = "A_IceGuyDie"; +//Sound functions + A_PlaySound = "A_PlaySound(string soundname[, int slot[, float volume[, bool looping[, float attenuation]]]])"; + A_PlaySoundEx = "A_PlaySoundEx(string soundname, string channel [, bool loop [, int attenuation]])"; + A_PlayWeaponSound = "A_PlayWeaponSound(string soundname)"; + A_ActiveSound = "A_ActiveSound"; + A_LoopActiveSound = "A_LoopActiveSound"; + A_FLoopActiveSound = "A_FLoopActiveSound"; + A_StopSound = "A_StopSound[(int slot)]"; + A_StopSoundEx = "A_StopSoundEx(string channel)"; + A_Pain = "A_Pain"; + A_Scream = "A_Scream"; + A_XScream = "A_XScream"; + A_PlayerScream = "A_PlayerScream"; + A_VileStart = "A_VileStart"; + A_BrainPain = "A_BrainPain"; + A_BrainAwake = "A_BrainAwake"; + A_BFGSound = "A_BFGSound"; +//Print actions + A_Print = "A_Print(string text[, float time[, string fontname]])"; + A_PrintBold = "A_PrintBold(string text[, float time[, string fontname]])"; + A_Log = "A_Log(string text)"; + A_LogInt = "A_LogInt(int number)"; +//Special actions + A_BossDeath = "A_BossDeath"; + A_KeenDie = "A_KeenDie[(int tag)]"; + A_BrainDie = "A_BrainDie"; + A_GetHurt = "A_GetHurt"; + A_KlaxonBlare = "A_KlaxonBlare"; + A_CheckTerrain = "A_CheckTerrain"; + A_SetBlend = "A_SetBlend(color blendcolor, float alpha, int duration[, color fadecolor])"; + A_CheckPlayerDone = "A_CheckPlayerDone"; + A_PlayerSkinCheck = "A_PlayerSkinCheck(str state)"; + A_SkullPop = "A_SkullPop[(string className)]"; +//Spawn functions + A_TossGib = "A_TossGib"; + A_SpawnDebris = "A_SpawnDebris(string type, bool translation)"; + A_SpawnItem = "A_SpawnItem(string type, int distance, float zpos, bool useammo, bool translation)"; + A_SpawnItemEx = "A_SpawnItemEx(string type, float xoffset, float yoffset, float zoffset, float xvelocity, float yvelocity, float zvelocity, float angle, int flags, int chance)"; +//State jumps + A_CheckCeiling = "A_CheckCeiling(int offset OR str \"state\")"; + A_CheckFloor = "A_CheckFloor(int offset OR str \"state\")"; + A_CheckFlag = "A_CheckFlag(string flagname, state label, int check_pointer = AAPTR_DEFAULT)"; + A_CheckSight = "A_CheckSight(int offset OR str \"state\")"; + A_CheckSightOrRange = "A_CheckSightOrRange(float distance, int offset OR str \"state\")"; + A_Jump = "A_Jump(int chance, int offset OR str \"state\", ...)"; + A_JumpIf = "A_JumpIf(expression, int offset OR str \"state\")"; + A_JumpIfArmorType = "A_JumpIfArmorType(string \"armortype\", str \"state\"[, int minimum])"; + A_JumpIfCloser = "A_JumpIfCloser(int distance, int offset OR str \"state\")"; + A_JumpIfHealthLower = "A_JumpIfHealthLower(int health, int offset OR str \"state\")"; + A_JumpIfInventory = "A_JumpIfInventory (string \"inventorytype\", int amount, int offset OR str \"state\" [, int owner])"; + A_JumpIfInTargetInventory = "A_JumpIfInTargetInventory(str \"item\", int count, int offset OR str \"state\" [, pointer forward])"; + A_JumpIfInTargetLOS = "A_JumpIfInTargetLOS(int offset OR str \"state\" [, float fov[, int flags[, float dist_max[, float dist_close]]]])"; + A_JumpIfMasterCloser = "A_JumpIfMasterCloser(int distance, int offset OR str \"state\")"; + A_JumpIfNoAmmo = "A_JumpIfNoAmmo(int offset OR str \"state\")"; + A_JumpIfTargetInLOS = "A_JumpIfTargetInLOS(int offset OR str \"state\" [, float fov[, int flags[, float dist_max[, float dist_close]]]])"; + A_JumpIfTracerCloser = "A_JumpIfTracerCloser (int distance, int offset OR str \"state\")"; +//Status changes + A_ActiveAndUnblock = "A_ActiveAndUnblock"; + A_ChangeFlag = "A_ChangeFlag(string flagname, bool value)"; + A_ChangeVelocity = "A_ChangeVelocity(float x, float y, float z, int flags)"; + A_ClearShadow = "A_ClearShadow"; + A_CopyFriendliness = "A_CopyFriendliness[(pointer copyfrom)]"; + A_DeQueueCorpse = "A_DeQueueCorpse"; + A_FadeIn = "A_FadeIn[(float increase_amount)]"; + A_FadeOut = "A_FadeOut[(float reduce_amount[, bool remove])]"; + A_FadeTo = "A_FadeTo(float target[, float amount[, bool remove]])"; + A_Fall = "A_Fall"; + A_Gravity = "A_Gravity"; + A_HideThing = "A_HideThing"; + A_LowGravity = "A_LowGravity"; + A_NoBlocking = "A_NoBlocking"; + A_NoGravity = "A_NoGravity"; + A_QueueCorpse = "A_QueueCorpse"; + A_RearrangePointers = "A_RearrangePointers(pointer target, pointer master, pointer tracer[, int flags])"; + A_Respawn = "A_Respawn[(bool fog)]"; + A_ScaleVelocity = "A_ScaleVelocity(float scale)"; + A_ScreamAndUnblock = "A_ScreamAndUnblock"; + A_SetAngle = "A_SetAngle(float angle)"; + A_SetArg = "A_SetArg(int position, int value)"; + A_SetFloat = "A_SetFloat"; + A_SetFloorClip = "A_SetFloorClip"; + A_SetInvulnerable = "A_SetInvulnerable"; + A_SetMass = "A_SetMass(int mass)"; + A_SetPitch = "A_SetPitch(float pitch[, int flags])"; + A_SetReflective = "A_SetReflective"; + A_SetReflectiveInvulnerable = "A_SetReflectiveInvulnerable"; + A_SetScale = "A_SetScale(float scaleX[, float scaleY])"; + A_SetShadow = "A_SetShadow"; + A_SetShootable = "A_SetShootable"; + A_SetSolid = "A_SetSolid"; + A_SetSpecial = "A_SetSpecial(int special, int arg0, int arg1, int arg2, int arg3, int arg4)"; + A_SetTranslucent = "A_SetTranslucent(float alpha, int mode)"; + A_SetUserVar = "A_SetUserVar(string name, int value)"; + A_TransferPointer = "A_TransferPointer(pointer source, pointer recipient, pointer sourcefield, pointer recipientfield[, int flags])"; + A_UnHideThing = "A_UnHideThing"; + A_UnsetFloat = "A_UnsetFloat"; + A_UnSetFloorClip = "A_UnSetFloorClip"; + A_UnSetInvulnerable = "A_UnSetInvulnerable"; + A_UnSetReflective = "A_UnSetReflective"; + A_UnSetReflectiveInvulnerable = "A_UnSetReflectiveInvulnerable"; + A_UnSetShootable = "A_UnSetShootable"; + A_UnsetSolid = "A_UnsetSolid"; +//Missile movement + A_SeekerMissile = "A_SeekerMissile(angle threshold, angle maxturnangle, int flags = 0, int chance = 50, int distance = 10)"; + A_Tracer = "A_Tracer"; + A_Tracer2 = "A_Tracer2"; + A_FaceTracer = "A_FaceTracer[(float angle)[, float pitch]]"; + A_Fire = "A_Fire[(int height)]"; + A_BishopMissileWeave = "A_BishopMissileWeave"; + A_CStaffMissileSlither = "A_CStaffMissileSlither"; + A_Weave = "A_Weave(int horzspeed, int vertspeed, float horzdist, float vertdist)"; + A_Warp = "A_Warp(int ptr_destination, float x-offset, float y-offset, float z-offset, float angle, int flags , state success_state)"; + A_Countdown = "A_Countdown"; + A_CountdownArg = "A_CountdownArg(int arg[, str targstate])"; + A_Stop = "A_Stop"; +//Inventory functions + A_GiveInventory = "A_GiveInventory(string type, int count[, pointer giveto])"; + A_GiveToTarget = "A_GiveToTarget(string type, int count[, pointer giveto])"; + A_TakeInventory = "A_TakeInventory(string type, int count[, int flags[, pointer takefrom]])"; + A_TakeFromTarget = "A_TakeFromTarget(string type, int count[, pointer takefrom])"; + A_DropInventory = "A_DropInventory(string type)"; + A_SelectWeapon = "A_SelectWeapon(string type)"; + A_RadiusGive = "A_RadiusGive(str item, fixed distance, int flags, int amount)"; +//Weapon functions + A_WeaponReady = "A_WeaponReady[(int flags)]"; + A_Lower = "A_Lower"; + A_Raise = "A_Raise"; + A_ReFire = "A_ReFire[(str \"state\")]"; + A_ClearReFire = "A_ClearReFire"; + A_GunFlash = "A_GunFlash[(str state[, int flags])]"; + A_CheckReload = "A_CheckReload"; + A_CheckForReload = "A_CheckForReload(int counter, str state[, bool dontincrement])"; + A_ResetReloadCounter = "A_ResetReloadCounter"; + A_Light = "A_Light(int intensity)"; + A_Light0 = "A_Light0"; + A_Light1 = "A_Light1"; + A_Light2 = "A_Light2"; + A_LightInverse = "A_LightInverse"; + A_Recoil = "A_Recoil(float force)"; + A_ZoomFactor = "A_ZoomFactor(float zoom [, int flags])"; + A_SetCrosshair = "A_SetCrosshair(int number)"; +//Weapon attack functions + A_Punch = "A_Punch"; + A_Saw = "A_Saw[(string fullsound [, string hitsound [, int damage [, string pufftype[, int flags[, float range[, float spread_xy[, float spread_z[, float lifesteal]]]]]]]])]"; + A_CustomPunch = "A_CustomPunch(int damage [, bool norandom [, int flags [, string pufftype, [float range[, float lifesteal]]]]])"; + A_FireBullets = "A_FireBullets(angle spread_horz, angle spread_vert, int numbullets, int damage [, string pufftype [,int flags [, float range]]])"; + A_FireCustomMissile = "A_FireCustomMissile(string missiletype [, angle angle [, bool useammo [, int spawnofs_horz [, int spawnheight [, bool aim[, angle pitch]]]]]])"; + A_RailAttack = "A_RailAttack(int damage [, int spawnofs_horz [, bool useammo [, color ringcolor [, color corecolor [, int flags [, int maxdiff [, str pufftype [, float spread_xy [, float spread_z [, fixed range [, int duration [, float sparsity [, float driftspeed [, str spawnclass]]]]]]]]]]]]]])"; + A_FireAssaultGun = "A_FireAssaultGun"; + A_FireBFG = "A_FireBFG"; + A_FireOldBFG = "A_FireOldBFG"; + A_FireShotgun = "A_FireShotgun"; + A_FireShotgun2 = "A_FireShotgun2"; + A_FireCGun = "A_FireCGun"; + A_FireMissile = "A_FireMissile"; + A_FirePlasma = "A_FirePlasma"; +//Script functions + ACS_NamedExecute = "ACS_NamedExecute(string script, int map, int s_arg1, int s_arg2, int s_arg3):bool"; + ACS_NamedSuspend = "ACS_NamedSuspend(string script, int map):bool"; + ACS_NamedTerminate = "ACS_NamedTerminate(string script, int map):bool"; + ACS_NamedLockedExecute = "ACS_NamedLockedExecute(string script, int map, int s_arg1, int s_arg2, int lock):bool"; + ACS_NamedLockedExecuteDoor = "ACS_NamedLockedExecuteDoor(string script, int map, int s_arg1, int s_arg2, int lock):bool"; + ACS_NamedExecuteWithResult = "ACS_NamedExecuteWithResult(string script, int s_arg1, int s_arg2, int s_arg3):int"; + ACS_NamedExecuteAlways = "ACS_NamedExecuteAlways(string script, int map, int s_arg1, int s_arg2, int s_arg3):bool"; +//Original Doom/Strife monster attacks + A_PosAttack = "A_PosAttack"; + A_SPosAttack = "A_SPosAttack"; + A_CPosAttack = "A_CPosAttack"; + A_CPosRefire = "A_CPosRefire"; + A_SpidRefire = "A_SpidRefire"; + A_TroopAttack = "A_TroopAttack"; + A_SargAttack = "A_SargAttack"; + A_HeadAttack = "A_HeadAttack"; + A_BruisAttack = "A_BruisAttack"; + A_SkullAttack = "A_SkullAttack[(int speed)]"; + A_BspiAttack = "A_BspiAttack"; + A_CyberAttack = "A_CyberAttack"; + A_PainAttack = "A_PainAttack[(string spawntype)[, float angle[, int flags[, int limit]]]]"; + A_DualPainAttack = "A_DualPainAttack[(string spawntype)]"; + A_PainDie = "A_PainDie[(string spawntype)]"; + A_SkelFist = "A_SkelFist"; + A_SkelMissile = "A_SkelMissile"; + A_FatAttack1 = "A_FatAttack1[(string spawntype)]"; + A_FatAttack2 = "A_FatAttack2[(string spawntype)]"; + A_FatAttack3 = "A_FatAttack3[(string spawntype)]"; + A_VileTarget = "A_VileTarget[(string type)]"; + A_VileAttack = "A_VileAttack(str sound, int initialdamage, int blastdamage, int blastradius, float thrustfactor, str damagetype)"; + A_BrainSpit = "A_BrainSpit[(string spawntype)]"; + A_SpawnFly = "A_SpawnFly[(string FogActor)]"; + A_SpawnSound = "A_SpawnSound"; + A_BrainScream = "A_BrainScream"; + A_BrainExplode = "A_BrainExplode"; + A_Mushroom = "A_Mushroom[(string spawntype [, int amount[, int flags[, float vrange[, float hrange]]]])]"; + A_M_Saw = "A_M_Saw[(string fullsound [, string hitsound [, int damage [, string pufftype]]])]"; + A_SentinelRefire = "A_SentinelRefire"; + A_BetaSkullAttack = "A_BetaSkullAttack"; +//Miscellaneous functions for Doom + A_Hoof = "A_Hoof"; + A_Metal = "A_Metal"; + A_BabyMetal = "A_BabyMetal"; + A_FatRaise = "A_FatRaise"; + A_SkelWhoosh = "A_SkelWhoosh"; + A_StartFire = "A_StartFire"; + A_FireCrackle = "A_FireCrackle"; + A_BFGSpray = "A_BFGSpray[(string flashtype [, int numrays [, int damage]])]"; + A_BarrelDestroy = "A_BarrelDestroy"; +//state keywords + Light = "Light(string lightname)"; + Offset = "Offset(int x, int y)"; } constants { + Actor; +//states: + Spawn:; + Idle:; + See:; + Melee:; + Missile:; + Pain:; + Death:; + XDeath:; + Burn:; + Ice:; + Disintegrate:; + Raise:; + Heal:; + Crash:; + Crush:; + Wound:; + Greetings:; + Yes:; + No:; + Active:; + Inactive:; +//State keywords + //Bright; + Fast; +//flow control + loop; + stop; + wait; + fail; + goto; +//pointers + AAPTR_DEFAULT; + AAPTR_NULL; + AAPTR_TARGET; + AAPTR_MASTER; + AAPTR_TRACER; + AAPTR_PLAYER; + AAPTR_PLAYER_GETTARGET; + AAPTR_PLAYER_GETCONVERSATION; + SXF_SETMASTER; + SXF_TRANSFERPOINTERS; +//weapon flags + WRF_NOBOB; + WRF_NOFIRE; + WRF_NOSWITCH; + WRF_NOPRIMARY; + WRF_NOSECONDARY; + WRF_ALLOWRELOAD; + WRF_ALLOWZOOM; + ZOOM_INSTANT; + ZOOM_NOSCALETURNING; + CPF_USEAMMO; + CPF_DAGGER; + CPF_PULLIN; + FBF_USEAMMO; + FBF_NORANDOM; + FBF_EXPLICITANGLE; + FBF_NOPITCH; + RGF_SILENT; + RGF_NOPIERCING; + RGF_EXPLICITANGLE; + RGF_FULLBRIGHT; +//monster flags + PAF_NOSKULLATTACK; + PAF_AIMFACING; + PAF_NOTARGET; +//misc flags + MSF_Standard; + MSF_Classic; + MSF_DontHurt; //combo flags Monster; Projectile; diff --git a/Help/gz_features.html b/Help/gz_features.html index 2a8473de..f9883d6e 100644 --- a/Help/gz_features.html +++ b/Help/gz_features.html @@ -27,7 +27,7 @@
  • Fog rendering (including colored fog in maps in UDMF format).
  • MD2 and MD3 models rendering in 2D and 3D modes.
  • Draw Rectangle, Draw Ellipse and Bridge modes.
  • -
  • Ability to move, insert and delete Things in Visual modes.
  • +
  • Ability to move, cut, copy, paste, insert and delete Things in Visual modes.
  • "Test Map from current position" feature.
  • "Sync camera position between 2D and 3D modes" feature.
  • "Place Things at cursor position in Visual Modes" feature.
  • diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj index a3c601cd..ff72b544 100644 --- a/Source/Core/Builder.csproj +++ b/Source/Core/Builder.csproj @@ -719,14 +719,17 @@ + + + diff --git a/Source/Core/Builder.sln b/Source/Core/Builder.sln index ed3d7843..38302107 100644 --- a/Source/Core/Builder.sln +++ b/Source/Core/Builder.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UDMFControls", "..\Plugins\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColorPicker", "..\Plugins\ColorPicker\ColorPicker.csproj", "{A4761900-0EA3-4FE4-A919-847FD5080EFC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommentsPanel", "..\Plugins\CommentsPanel\CommentsPanel.csproj", "{58BD8A5B-1B48-435D-8473-A92F27D06C49}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -83,6 +85,16 @@ Global {A4761900-0EA3-4FE4-A919-847FD5080EFC}.Release|Mixed Platforms.Build.0 = Release|x86 {A4761900-0EA3-4FE4-A919-847FD5080EFC}.Release|x86.ActiveCfg = Release|x86 {A4761900-0EA3-4FE4-A919-847FD5080EFC}.Release|x86.Build.0 = Release|x86 + {58BD8A5B-1B48-435D-8473-A92F27D06C49}.Debug|Any CPU.ActiveCfg = Debug|x86 + {58BD8A5B-1B48-435D-8473-A92F27D06C49}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {58BD8A5B-1B48-435D-8473-A92F27D06C49}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {58BD8A5B-1B48-435D-8473-A92F27D06C49}.Debug|x86.ActiveCfg = Debug|x86 + {58BD8A5B-1B48-435D-8473-A92F27D06C49}.Debug|x86.Build.0 = Debug|x86 + {58BD8A5B-1B48-435D-8473-A92F27D06C49}.Release|Any CPU.ActiveCfg = Release|x86 + {58BD8A5B-1B48-435D-8473-A92F27D06C49}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {58BD8A5B-1B48-435D-8473-A92F27D06C49}.Release|Mixed Platforms.Build.0 = Release|x86 + {58BD8A5B-1B48-435D-8473-A92F27D06C49}.Release|x86.ActiveCfg = Release|x86 + {58BD8A5B-1B48-435D-8473-A92F27D06C49}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/Core/Config/ArgumentInfo.cs b/Source/Core/Config/ArgumentInfo.cs index e9915551..348f2583 100644 --- a/Source/Core/Config/ArgumentInfo.cs +++ b/Source/Core/Config/ArgumentInfo.cs @@ -107,6 +107,8 @@ namespace CodeImp.DoomBuilder.Config this.title = "Argument " + (argindex + 1); this.type = 0; this.enumlist = new EnumList(); + //mxd + this.defaultValue = 0; } #endregion diff --git a/Source/Core/Controls/ScriptDocumentTab.cs b/Source/Core/Controls/ScriptDocumentTab.cs index 5abbd12e..0685b28e 100644 --- a/Source/Core/Controls/ScriptDocumentTab.cs +++ b/Source/Core/Controls/ScriptDocumentTab.cs @@ -58,14 +58,6 @@ namespace CodeImp.DoomBuilder.Controls protected ScriptEditorControl editor; //mxd protected ComboBox navigator; - protected enum ScriptTypes : int - { - ACS = 0, - MODELDEF = 1, - DECORATE = 2, - } - private string[] knownScriptTypes = { "ZDoom ACS script", "GZDoom MODELDEF", "ZDoom DECORATE" }; - protected string[] KNOWN_SCRIPT_TYPES { get { return knownScriptTypes; } } // Derived classes must set this! protected ScriptConfiguration config; @@ -325,110 +317,82 @@ namespace CodeImp.DoomBuilder.Controls //mxd protected void updateNavigator() { //mxd. known script type? - if (Array.IndexOf(KNOWN_SCRIPT_TYPES, config.Description) != -1) - navigator.Enabled = updateNavigator(new MemoryStream(editor.GetText()), config.Description); - if(navigator.Enabled) + if (Array.IndexOf(ScriptTypes.TYPES, config.Description) != -1) { + updateNavigator(new MemoryStream(editor.GetText()), config.Description); + navigator.Enabled = true; navigator.SelectedIndexChanged += new EventHandler(navigator_SelectedIndexChanged); + }else{ + navigator.Items.Clear(); + navigator.Enabled = false; + } } //mxd - private bool updateNavigator(MemoryStream stream, string scriptType) { - if (scriptType == KNOWN_SCRIPT_TYPES[(int)ScriptTypes.ACS]) //ZDoom ACS script - return updateNavigatorAcs(); - if (scriptType == KNOWN_SCRIPT_TYPES[(int)ScriptTypes.MODELDEF]) //GZDoom MODELDEF - return updateNavigatorModeldef(stream); - if (scriptType == KNOWN_SCRIPT_TYPES[(int)ScriptTypes.DECORATE]) - return updateNavigatorDecorate(stream); - return false; + private void updateNavigator(MemoryStream stream, string scriptType) { + if (scriptType == ScriptTypes.TYPES[(int)ScriptType.ACS]) { + updateNavigatorAcs(stream); + } else if (scriptType == ScriptTypes.TYPES[(int)ScriptType.MODELDEF]) { + updateNavigatorModeldef(stream); + } else if (scriptType == ScriptTypes.TYPES[(int)ScriptType.DECORATE]) { + updateNavigatorDecorate(stream); + } } //mxd - private bool updateNavigatorDecorate(MemoryStream stream) { - if (stream == null) return false; - - string selectedItem = ""; - int selectedIndex = 0; - if (navigator.SelectedIndex != -1) selectedItem = navigator.Text; + private void updateNavigatorDecorate(MemoryStream stream) { + if (stream == null) return; navigator.Items.Clear(); DecorateParserSE parser = new DecorateParserSE(); parser.Parse(stream, "DECORATE"); - if (parser.Actors.Count == 0) - return false; + if (parser.Actors.Count == 0) return; - ScriptItem[] models = new ScriptItem[parser.Actors.Count]; - int i = 0; - foreach (ScriptItem si in parser.Actors) { - models[i++] = si; - if (si.Name == selectedItem) selectedIndex = i - 1; - } - navigator.Items.AddRange(models); - return true; + navigator.Items.AddRange(parser.Actors.ToArray()); } //mxd - private bool updateNavigatorModeldef(MemoryStream stream) { - if (stream == null) return false; - - string selectedItem = ""; - int selectedIndex = 0; - if (navigator.SelectedIndex != -1) selectedItem = navigator.Text; + private void updateNavigatorModeldef(MemoryStream stream) { + if (stream == null) return; navigator.Items.Clear(); ModeldefParserSE parser = new ModeldefParserSE(); parser.Parse(stream, "MODELDEF"); - if (parser.Models.Count == 0) - return false; + if (parser.Models.Count == 0) return; - ScriptItem[] models = new ScriptItem[parser.Models.Count]; - int i = 0; - foreach (ScriptItem si in parser.Models) { - models[i++] = si; - if (si.Name == selectedItem) selectedIndex = i - 1; - } - navigator.Items.AddRange(models); - return true; + navigator.Items.AddRange(parser.Models.ToArray()); } //mxd - private bool updateNavigatorAcs() { - string selectedItem = ""; - int selectedIndex = 0; - if (navigator.SelectedIndex != -1) selectedItem = navigator.Text; - + private void updateNavigatorAcs(MemoryStream stream) { + if (stream == null) return; + navigator.Items.Clear(); - //add named scripts - int i = 0; - if (General.Map.UDMF) { - ScriptItem[] namedScripts = new ScriptItem[General.Map.NamedScripts.Count]; - foreach (ScriptItem si in General.Map.NamedScripts) { - namedScripts[i++] = si; - if (si.Name == selectedItem) selectedIndex = i - 1; - } - navigator.Items.AddRange(namedScripts); - } + AcsParserSE parser = new AcsParserSE(); + parser.Parse(stream, "ACS"); - //add numbered scripts - ScriptItem[] numberedScripts = new ScriptItem[General.Map.NumberedScripts.Count]; - int c = 0; - foreach (ScriptItem si in General.Map.NumberedScripts) { - numberedScripts[c++] = si; - if (si.Name == selectedItem) selectedIndex = i - 1 + c; - } - navigator.Items.AddRange(numberedScripts); + if (parser.NamedScripts.Count == 0 && parser.NumberedScripts.Count == 0) return; - if (navigator.Items.Count > 0) { - navigator.SelectedIndex = selectedIndex; - return true; - } - return false; + if(General.Map.UDMF) + navigator.Items.AddRange(parser.NamedScripts.ToArray()); + + navigator.Items.AddRange(parser.NumberedScripts.ToArray()); } + //mxd + internal ScriptType VerifyScriptType() { + ScriptTypeParserSE parser = new ScriptTypeParserSE(); + if (parser.Parse(new MemoryStream(editor.GetText()), config.Description)) { + if (parser.ScriptType != (int)ScriptType.UNKNOWN && config.Description != ScriptTypes.TYPES[(int)parser.ScriptType]) + return parser.ScriptType; + } + return ScriptType.UNKNOWN; + } + #endregion #region ================== Events diff --git a/Source/Core/Controls/ScriptEditorPanel.cs b/Source/Core/Controls/ScriptEditorPanel.cs index 82bf9b97..fb53f37d 100644 --- a/Source/Core/Controls/ScriptEditorPanel.cs +++ b/Source/Core/Controls/ScriptEditorPanel.cs @@ -33,6 +33,7 @@ using CodeImp.DoomBuilder.IO; using System.Globalization; using System.IO; using CodeImp.DoomBuilder.Compilers; +using CodeImp.DoomBuilder.GZBuilder.Data; #endregion @@ -521,7 +522,19 @@ namespace CodeImp.DoomBuilder.Controls ScriptFileDocumentTab t = new ScriptFileDocumentTab(this, foundconfig); if(t.Open(filename)) { - // Mark any errors this script may have + //mxd + ScriptType st = t.VerifyScriptType(); + if (st != ScriptType.UNKNOWN) { + string cfgType = ScriptTypes.TYPES[(int)st]; + foreach (ScriptConfiguration cfg in scriptconfigs) { + if (cfg.Description == cfgType) { + t.ChangeScriptConfig(cfg); + break; + } + } + } + + // Mark any errors this script may have if(compilererrors != null) t.MarkScriptErrors(compilererrors); diff --git a/Source/Core/Controls/ScriptFileDocumentTab.cs b/Source/Core/Controls/ScriptFileDocumentTab.cs index e171f6e9..36c2da89 100644 --- a/Source/Core/Controls/ScriptFileDocumentTab.cs +++ b/Source/Core/Controls/ScriptFileDocumentTab.cs @@ -31,6 +31,9 @@ using CodeImp.DoomBuilder.Types; using CodeImp.DoomBuilder.IO; using System.IO; using CodeImp.DoomBuilder.Compilers; +//mxd +using CodeImp.DoomBuilder.GZBuilder.Data; +using CodeImp.DoomBuilder.GZBuilder.GZDoom; #endregion @@ -70,7 +73,7 @@ namespace CodeImp.DoomBuilder.Controls SetTitle("Untitled" + ext); editor.ClearUndoRedo(); //mxd - navigator.Enabled = Array.IndexOf(KNOWN_SCRIPT_TYPES, config.Description) != -1; + navigator.Enabled = Array.IndexOf(ScriptTypes.TYPES, config.Description) != -1; } // Disposer @@ -190,10 +193,10 @@ namespace CodeImp.DoomBuilder.Controls // This opens a file and returns true when successful public bool Open(string filepathname) { - try + try { // Read the file - editor.SetText(File.ReadAllBytes(filepathname)); + editor.SetText(File.ReadAllBytes(filepathname)); } catch(Exception e) { @@ -208,6 +211,7 @@ namespace CodeImp.DoomBuilder.Controls this.filepathname = filepathname; SetTitle(Path.GetFileName(filepathname)); editor.ClearUndoRedo(); + return true; } diff --git a/Source/Core/Controls/ScriptLumpDocumentTab.cs b/Source/Core/Controls/ScriptLumpDocumentTab.cs index 4c6088ce..8af2d78b 100644 --- a/Source/Core/Controls/ScriptLumpDocumentTab.cs +++ b/Source/Core/Controls/ScriptLumpDocumentTab.cs @@ -87,6 +87,8 @@ namespace CodeImp.DoomBuilder.Controls { editor.SetText(stream.ToArray()); editor.ClearUndoRedo(); + //mxd + updateNavigator(); } // Done @@ -117,10 +119,6 @@ namespace CodeImp.DoomBuilder.Controls // Feed errors to panel panel.ShowErrors(General.Map.Errors); - - //mxd - if (config.Description == KNOWN_SCRIPT_TYPES[(int)ScriptTypes.ACS] && General.Map.Errors.Count == 0) - General.Map.UpdateScriptNames(); } // Implicit save diff --git a/Source/Core/Data/DataManager.cs b/Source/Core/Data/DataManager.cs index 3a5d5b7c..13717210 100644 --- a/Source/Core/Data/DataManager.cs +++ b/Source/Core/Data/DataManager.cs @@ -1587,7 +1587,7 @@ namespace CodeImp.DoomBuilder.Data gldefsEntries.Add(thingType, parser.LightsByName[e.Value]); } } else { - GZBuilder.GZGeneral.LogAndTraceWarning("Got GLDEFS for class '" + e.Key + "', but haven't found such class in Decorate"); + GZBuilder.GZGeneral.LogAndTraceWarning("Got GLDEFS light for class '" + e.Key + "', but haven't found such class in Decorate"); } } diff --git a/Source/Core/GZBuilder/Data/ScriptItem.cs b/Source/Core/GZBuilder/Data/ScriptItem.cs index 48f2f8ac..84596bc2 100644 --- a/Source/Core/GZBuilder/Data/ScriptItem.cs +++ b/Source/Core/GZBuilder/Data/ScriptItem.cs @@ -34,8 +34,19 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data { } internal static int SortByName(ScriptItem i1, ScriptItem i2) { + if (i1.Name == i2.Name) return 0; + if (i1.Name.ToUpper()[0] > i2.Name.ToUpper()[0]) return 1; - if (i1.Name.ToUpper()[0] == i2.Name.ToUpper()[0]) return 0; + if (i1.Name.ToUpper()[0] == i2.Name.ToUpper()[0]) { + int len = Math.Min(i1.Name.Length, i2.Name.Length); + for (int i = 0; i < len; i++) { + if (i1.Name.ToUpper()[i] > i2.Name.ToUpper()[i]) return 1; + if (i1.Name.ToUpper()[i] < i2.Name.ToUpper()[i]) return -1; + } + + if (i1.Name.Length > i2.Name.Length) return 1; + return -1; + } return -1; } diff --git a/Source/Core/GZBuilder/Data/ScriptType.cs b/Source/Core/GZBuilder/Data/ScriptType.cs new file mode 100644 index 00000000..2a6b6b2d --- /dev/null +++ b/Source/Core/GZBuilder/Data/ScriptType.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace CodeImp.DoomBuilder.GZBuilder.Data { + internal enum ScriptType : int { + UNKNOWN = 0, + ACS = 1, + MODELDEF = 2, + DECORATE = 3, + } + + internal struct ScriptTypes { + private static string[] knownScriptTypes = { "UNKNOWN SCRIPT", "ZDoom ACS script", "GZDoom MODELDEF", "ZDoom DECORATE" }; + internal static string[] TYPES { get { return knownScriptTypes; } } + } +} diff --git a/Source/Core/GZBuilder/Data/ThingCopyData.cs b/Source/Core/GZBuilder/Data/ThingCopyData.cs new file mode 100644 index 00000000..58e49e3e --- /dev/null +++ b/Source/Core/GZBuilder/Data/ThingCopyData.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Text; +using CodeImp.DoomBuilder.Map; +using CodeImp.DoomBuilder.Geometry; +using CodeImp.DoomBuilder.Rendering; + +namespace CodeImp.DoomBuilder.GZBuilder.Data { + public sealed class ThingCopyData { + // Properties + private int type; + private Vector3D pos; + private int angledoom; // Angle as entered / stored in file + private Dictionary flags; + private int tag; + private int action; + private int[] args; + private UniFields fields; + + public Vector3D Position { get { return pos; } } + + public ThingCopyData(Thing t) { + type = t.Type; + angledoom = t.AngleDoom; + pos = t.Position; + flags = new Dictionary(t.Flags); //t.Flags; + tag = t.Tag; + action = t.Action; + args = (int[])t.Args.Clone(); + fields = new UniFields(t, t.Fields); + } + + public void ApplyTo(Thing t) { + t.Type = type; + t.Rotate(angledoom); + t.Move(pos); + + foreach(KeyValuePair group in flags) + t.SetFlag(group.Key, group.Value); + + t.Tag = tag; + t.Action = action; + + foreach(int i in args) + t.Args[i] = args[i]; + + foreach (KeyValuePair group in fields) { + if (t.Fields.ContainsKey(group.Key)) + t.Fields[group.Key] = group.Value; + else + t.Fields.Add(group.Key, group.Value); + } + + t.UpdateConfiguration(); + } + } +} diff --git a/Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs b/Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs new file mode 100644 index 00000000..fd708b1e --- /dev/null +++ b/Source/Core/GZBuilder/GZDoom/ScriptTypeParserSE.cs @@ -0,0 +1,79 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Text; + +using CodeImp.DoomBuilder.ZDoom; +using CodeImp.DoomBuilder.GZBuilder.Data; + +//mxd. Parser used to determine which script type given text is. +namespace CodeImp.DoomBuilder.GZBuilder.GZDoom { + internal sealed class ScriptTypeParserSE :ZDTextParser { + private ScriptType scriptType; + internal ScriptType ScriptType { get { return scriptType; } } + + internal ScriptTypeParserSE() { + scriptType = ScriptType.UNKNOWN; + } + + public override bool Parse(Stream stream, string sourcefilename) { + base.Parse(stream, sourcefilename); + + // Continue until at the end of the stream + while (SkipWhitespace(true)) { + string token = ReadToken(); + + if (!string.IsNullOrEmpty(token)) { + token = token.ToUpperInvariant(); + + if (token == "MODEL") { + SkipWhitespace(true); + token = ReadToken(); //should be model name + SkipWhitespace(true); + token = ReadToken();//should be opening brace + + if (token == "{") { + scriptType = ScriptType.MODELDEF; + return true; + } + + }else if(token == "SCRIPT"){ + SkipWhitespace(true); + token = ReadToken(); //should be script name or number + SkipWhitespace(true); + token = ReadToken(); //should be script parameters/type + SkipWhitespace(true); + token = ReadToken(); //should be opening brace + + if (token == "{") { + scriptType = ScriptType.ACS; + return true; + } + + }else if(token == "ACTOR"){ + SkipWhitespace(true); + token = ReadToken(); //should be actor name + + SkipWhitespace(true); + token = ReadToken(); + + if (token == ":" || token == "{" || token == "REPLACES") { + scriptType = ScriptType.DECORATE; + return true; + } + + SkipWhitespace(true); + token = ReadToken(); //should be actor name + + if (token == "{") { + scriptType = ScriptType.DECORATE; + return true; + } + } + } + } + + return false; + } + } +} diff --git a/Source/Core/VisualModes/VisualMode.cs b/Source/Core/VisualModes/VisualMode.cs index b22251b2..ae343356 100644 --- a/Source/Core/VisualModes/VisualMode.cs +++ b/Source/Core/VisualModes/VisualMode.cs @@ -407,31 +407,31 @@ namespace CodeImp.DoomBuilder.VisualModes protected void moveSelectedThingsLeft() { moveSelectedThings(new Vector2D(0f, -General.Map.Grid.GridSize), false); } - + //mxd [BeginAction("movethingright", BaseAction = true)] protected void moveSelectedThingsRight() { moveSelectedThings(new Vector2D(0f, General.Map.Grid.GridSize), false); } - + //mxd [BeginAction("movethingfwd", BaseAction = true)] protected void moveSelectedThingsForward() { moveSelectedThings(new Vector2D(-General.Map.Grid.GridSize, 0f), false); } - + //mxd [BeginAction("movethingback", BaseAction = true)] protected void moveSelectedThingsBackward() { moveSelectedThings(new Vector2D(General.Map.Grid.GridSize, 0f), false); } - + //mxd [BeginAction("placethingatcursor", BaseAction = true)] protected void placeThingAtCursor() { - Vector2D hitCoords = getHitPosition(); - if (!hitCoords.IsFinite()) { + Vector2D hitpos = getHitPosition(); + if (!hitpos.IsFinite()) { General.Interface.DisplayStatus(StatusType.Warning, "Cannot place Thing here"); return; } - moveSelectedThings(new Vector2D((float)Math.Round(hitCoords.x), (float)Math.Round(hitCoords.y)), true); + moveSelectedThings(new Vector2D((float)Math.Round(hitpos.x), (float)Math.Round(hitpos.y)), true); } //mxd. @@ -458,15 +458,14 @@ namespace CodeImp.DoomBuilder.VisualModes return hitCoords; } - //mxd. this checks intersection between line and plane + //mxd. This checks intersection between line and plane protected Vector2D getIntersection(Vector3D start, Vector3D end, Vector3D planeCenter, Vector3D planeNormal) { Vector3D delta = new Vector3D(planeCenter.x - start.x, planeCenter.y - start.y, planeCenter.z - start.z); return start + Vector3D.DotProduct(planeNormal, delta) / Vector3D.DotProduct(planeNormal, end - start) * (end - start); } - //should move selected things in specified direction + //mxd. Should move selected things in specified direction protected abstract void moveSelectedThings(Vector2D direction, bool absolutePosition); - //end of mxd :) #endregion diff --git a/Source/Core/Windows/ErrorsForm.Designer.cs b/Source/Core/Windows/ErrorsForm.Designer.cs index e4aca63f..181b4790 100644 --- a/Source/Core/Windows/ErrorsForm.Designer.cs +++ b/Source/Core/Windows/ErrorsForm.Designer.cs @@ -162,6 +162,7 @@ namespace CodeImp.DoomBuilder.Windows this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(680, 300); this.Name = "ErrorsForm"; + this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Errors and Warnings"; this.Shown += new System.EventHandler(this.ErrorsForm_Shown); diff --git a/Source/Core/ZDoom/ActorStructure.cs b/Source/Core/ZDoom/ActorStructure.cs index 8beec4d1..caec72a8 100644 --- a/Source/Core/ZDoom/ActorStructure.cs +++ b/Source/Core/ZDoom/ActorStructure.cs @@ -50,6 +50,7 @@ namespace CodeImp.DoomBuilder.ZDoom // Inheriting private ActorStructure baseclass; private bool skipsuper; + private bool haveBaseClass;//mxd // Flags private Dictionary flags; @@ -110,7 +111,8 @@ namespace CodeImp.DoomBuilder.ZDoom token = token.ToLowerInvariant(); if(token == ":") { - // The next token must be the class to inherit from + haveBaseClass = true; //mxd + // The next token must be the class to inherit from parser.SkipWhitespace(true); inheritclass = parser.StripTokenQuotes(parser.ReadToken()); if(string.IsNullOrEmpty(inheritclass) || parser.IsSpecialToken(inheritclass)) @@ -122,8 +124,10 @@ namespace CodeImp.DoomBuilder.ZDoom { // Find the actor to inherit from baseclass = parser.GetArchivedActorByName(inheritclass); - if(baseclass == null) - General.ErrorLogger.Add(ErrorType.Warning, "Unable to find the DECORATE class '" + inheritclass + "' to inherit from, while parsing '" + classname + "'"); + + //mxd. Fat chances are that this actor is not ment to be placed in the map, so we'll better check baseclass AFTER we parsed doomednum + //if(baseclass == null) + //General.ErrorLogger.Add(ErrorType.Warning, "Unable to find the DECORATE class '" + inheritclass + "' to inherit from, while parsing '" + classname + "'"); } } else if(token == "replaces") @@ -170,6 +174,10 @@ namespace CodeImp.DoomBuilder.ZDoom return; } } + + //mxd. Check if baseclass is valid + if (haveBaseClass && doomednum > -1 && baseclass == null) + General.ErrorLogger.Add(ErrorType.Warning, "Unable to find the DECORATE class '" + inheritclass + "' to inherit from, while parsing '" + classname + ":" + doomednum +"'"); // Now parse the contents of actor structure string previoustoken = ""; diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs index 54243578..3490bddf 100644 --- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs @@ -33,6 +33,7 @@ using CodeImp.DoomBuilder.Editing; using CodeImp.DoomBuilder.Actions; using CodeImp.DoomBuilder.VisualModes; using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.GZBuilder.Data; #endregion @@ -84,6 +85,8 @@ namespace CodeImp.DoomBuilder.BuilderModes // List of selected objects when an action is performed private List selectedobjects; + //mxd. Used in Cut/PasteSelection actions + private List copyBuffer; #endregion @@ -133,6 +136,8 @@ namespace CodeImp.DoomBuilder.BuilderModes // Initialize this.gravity = new Vector3D(0.0f, 0.0f, 0.0f); this.selectedobjects = new List(); + //mxd + this.copyBuffer = new List(); // We have no destructor GC.SuppressFinalize(this); @@ -427,57 +432,82 @@ namespace CodeImp.DoomBuilder.BuilderModes //mxd protected override void moveSelectedThings(Vector2D direction, bool absolutePosition) { - List things = GetSelectedVisualThings(true); + List visualThings = GetSelectedVisualThings(true); - if (things.Count == 0) { + if (visualThings.Count == 0) { General.Interface.DisplayStatus(StatusType.Warning, "Select some Things first!"); return; } + PreAction(UndoGroup.SectorHeightChange); + + Vector3D[] coords = new Vector3D[visualThings.Count]; + for (int i = 0; i < visualThings.Count; i++) + coords[i] = visualThings[i].Thing.Position; + + //move things... + Vector3D[] translatedCoords = translateCoordinates(coords, direction, absolutePosition); + for (int i = 0; i < visualThings.Count; i++) { + BaseVisualThing t = visualThings[i] as BaseVisualThing; + t.OnMove(translatedCoords[i]); + } + + PostAction(); + } + + //mxd + private Vector3D[] translateCoordinates(Vector3D[] coordinates, Vector2D direction, bool absolutePosition) { + if (coordinates.Length == 0) return null; + + direction.x = (float)Math.Round(direction.x); + direction.y = (float)Math.Round(direction.y); + + Vector3D[] translatedCoords = new Vector3D[coordinates.Length]; + //move things... if (!absolutePosition) { //...relatively (that's easy) int camAngle = (int)Math.Round(General.Map.VisualCamera.AngleXY * 180 / Math.PI); int sector = (int)(General.ClampAngle(camAngle - 45f) / 90f); direction = direction.GetRotated((float)(sector * Math.PI / 2f)); - for (int i = 0; i < things.Count; i++) { - BaseVisualThing t = things[i] as BaseVisualThing; - t.OnMove(t.Thing.Position + new Vector3D(direction)); - } - } else { //...to specified location preserving relative positioning (that's harder) - if (things.Count == 1) {//just move it there - BaseVisualThing t = things[0] as BaseVisualThing; - t.OnMove(new Vector3D(direction.x, direction.y, t.Thing.Position.z)); - return; - } + for (int i = 0; i < coordinates.Length; i++) + translatedCoords[i] = coordinates[i] + new Vector3D(direction); - //we need some reference - float minX = things[0].Thing.Position.x; - float maxX = minX; - float minY = things[0].Thing.Position.y; - float maxY = minY; - - //get bounding coordinates for selected things - for (int i = 1; i < things.Count; i++) { - if (things[i].Thing.Position.x < minX) - minX = things[i].Thing.Position.x; - else if (things[i].Thing.Position.x > maxX) - maxX = things[i].Thing.Position.x; - - if (things[i].Thing.Position.y < minY) - minY = things[i].Thing.Position.y; - else if (things[i].Thing.Position.y > maxY) - maxY = things[i].Thing.Position.y; - } - - Vector2D selectionCenter = new Vector2D(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); - - //move them - for (int i = 0; i < things.Count; i++) { - BaseVisualThing t = things[i] as BaseVisualThing; - t.OnMove(new Vector3D(direction.x - (selectionCenter.x - t.Thing.Position.x), direction.y - (selectionCenter.y - t.Thing.Position.y), t.Thing.Position.z)); - } + return translatedCoords; } + + //...to specified location preserving relative positioning (that's harder) + if (coordinates.Length == 1) {//just move it there + translatedCoords[0] = new Vector3D(direction.x, direction.y, coordinates[0].z); + return translatedCoords; + } + + //we need some reference + float minX = coordinates[0].x; + float maxX = minX; + float minY = coordinates[0].y; + float maxY = minY; + + //get bounding coordinates for selected things + for (int i = 1; i < coordinates.Length; i++) { + if (coordinates[i].x < minX) + minX = coordinates[i].x; + else if (coordinates[i].x > maxX) + maxX = coordinates[i].x; + + if (coordinates[i].y < minY) + minY = coordinates[i].y; + else if (coordinates[i].y > maxY) + maxY = coordinates[i].y; + } + + Vector2D selectionCenter = new Vector2D(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); + + //move them + for (int i = 0; i < coordinates.Length; i++) + translatedCoords[i] = new Vector3D((float)Math.Round(direction.x - (selectionCenter.x - coordinates[i].x)), (float)Math.Round(direction.y - (selectionCenter.y - coordinates[i].y)), (float)Math.Round(coordinates[i].z)); + + return translatedCoords; } #endregion @@ -504,6 +534,7 @@ namespace CodeImp.DoomBuilder.BuilderModes public override void OnDisengage() { base.OnDisengage(); + copyBuffer.Clear(); //mxd General.Map.Map.Update(); } @@ -962,7 +993,7 @@ namespace CodeImp.DoomBuilder.BuilderModes //mxd. Copied from BuilderModes.ThingsMode // This creates a new thing - private Thing InsertThing(Vector2D pos) { + private Thing CreateThing(Vector2D pos) { if (pos.x < General.Map.Config.LeftBoundary || pos.x > General.Map.Config.RightBoundary || pos.y > General.Map.Config.TopBoundary || pos.y < General.Map.Config.BottomBoundary) { General.Interface.DisplayStatus(StatusType.Warning, "Failed to insert thing: outside of map boundaries."); @@ -1345,7 +1376,7 @@ namespace CodeImp.DoomBuilder.BuilderModes //mxd. now we can actually insert things in Visual modes [BeginAction("insertitem", BaseAction = true)] - public void Insert() { + public void InsertThing() { Vector2D hitpos = getHitPosition(); if (!hitpos.IsFinite()) { @@ -1358,7 +1389,7 @@ namespace CodeImp.DoomBuilder.BuilderModes General.Map.UndoRedo.CreateUndo("Insert thing"); - Thing t = InsertThing(new Vector2D(hitpos.x, hitpos.y)); + Thing t = CreateThing(new Vector2D(hitpos.x, hitpos.y)); if (t == null) { General.Map.UndoRedo.WithdrawUndo(); @@ -1379,7 +1410,7 @@ namespace CodeImp.DoomBuilder.BuilderModes } [BeginAction("deleteitem", BaseAction = true)] //mxd. now we can actually delete things in Visual modes - public void Delete() { + public void DeleteSelectedThings() { List objs = GetSelectedObjects(false, false, true); if (objs.Count == 0) return; @@ -1398,6 +1429,73 @@ namespace CodeImp.DoomBuilder.BuilderModes PostAction(); } + + //mxd + [BeginAction("copyselection", BaseAction = true)] + public void CopySelection() { + List objs = GetSelectedObjects(false, false, true); + if (objs.Count == 0) { + General.Interface.DisplayStatus(StatusType.Warning, "Nothing to copy, select some Things first!"); + return; + } + + copyBuffer.Clear(); + foreach (IVisualEventReceiver i in objs) { + VisualThing vt = i as VisualThing; + if (vt != null) copyBuffer.Add(new ThingCopyData(vt.Thing)); + } + General.Interface.DisplayStatus(StatusType.Info, "Copied " + copyBuffer.Count + " Things"); + } + + //mxd + [BeginAction("cutselection", BaseAction = true)] + public void CutSelection() { + CopySelection(); + DeleteSelectedThings(); + } + + //mxd. We'll just use currently selected objects + [BeginAction("pasteselection", BaseAction = true)] + public void PasteSelection() { + if (copyBuffer.Count == 0) { + General.Interface.DisplayStatus(StatusType.Warning, "Nothing to paste, cut or copy some Things first!"); + return; + } + + Vector2D hitpos = getHitPosition(); + + if (!hitpos.IsFinite()) { + General.Interface.DisplayStatus(StatusType.Warning, "Cannot paste here!"); + return; + } + + General.Map.UndoRedo.ClearAllRedos(); + string rest = copyBuffer.Count + " thing" + (copyBuffer.Count > 1 ? "s." : "."); + General.Map.UndoRedo.CreateUndo("Paste " + rest); + General.Interface.DisplayStatus(StatusType.Info, "Pasted " + rest); + + PreActionNoChange(); + ClearSelection(); + + //get translated positions + Vector3D[] coords = new Vector3D[copyBuffer.Count]; + for (int i = 0; i < copyBuffer.Count; i++) + coords[i] = copyBuffer[i].Position; + + Vector3D[] translatedCoords = translateCoordinates(coords, hitpos, true); + + //create things from copyBuffer + for (int i = 0; i < copyBuffer.Count; i++) { + Thing t = CreateThing(new Vector2D()); + if (t != null) { + copyBuffer[i].ApplyTo(t); + t.Move(translatedCoords[i]); + //add thing to blockmap + blockmap.AddThing(t); + } + } + PostAction(); + } #endregion } diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs index c4935e05..217f793c 100644 --- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs @@ -480,7 +480,6 @@ namespace CodeImp.DoomBuilder.BuilderModes Thing.Move(newPosition); mode.SetActionResult("Changed thing position to " + Thing.Position.ToString() + "."); this.Changed = true; - Rebuild(); } #endregion diff --git a/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualMode.cs b/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualMode.cs index 4f56c0fe..59c31800 100644 --- a/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualMode.cs +++ b/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualMode.cs @@ -34,6 +34,7 @@ using CodeImp.DoomBuilder.Editing; using CodeImp.DoomBuilder.Actions; using CodeImp.DoomBuilder.VisualModes; using CodeImp.DoomBuilder.Config; +using CodeImp.DoomBuilder.GZBuilder.Data; #endregion @@ -89,6 +90,8 @@ namespace CodeImp.DoomBuilder.GZDoomEditing // List of selected objects when an action is performed private List selectedobjects; + //mxd. Used in Cut/PasteSelection actions + private List copyBuffer; #endregion @@ -138,6 +141,8 @@ namespace CodeImp.DoomBuilder.GZDoomEditing // Initialize this.gravity = new Vector3D(0.0f, 0.0f, 0.0f); this.selectedobjects = new List(); + //mxd + this.copyBuffer = new List(); // We have no destructor GC.SuppressFinalize(this); @@ -444,57 +449,82 @@ namespace CodeImp.DoomBuilder.GZDoomEditing //mxd protected override void moveSelectedThings(Vector2D direction, bool absolutePosition) { - List things = GetSelectedVisualThings(true); + List visualThings = GetSelectedVisualThings(true); - if (things.Count == 0) { + if (visualThings.Count == 0) { General.Interface.DisplayStatus(StatusType.Warning, "Select some Things first!"); return; } + PreAction(UndoGroup.SectorHeightChange); + + Vector3D[] coords = new Vector3D[visualThings.Count]; + for (int i = 0; i < visualThings.Count; i++) + coords[i] = visualThings[i].Thing.Position; + + //move things... + Vector3D[] translatedCoords = translateCoordinates(coords, direction, absolutePosition); + for (int i = 0; i < visualThings.Count; i++) { + BaseVisualThing t = visualThings[i] as BaseVisualThing; + t.OnMove(translatedCoords[i]); + } + + PostAction(); + } + + //mxd + private Vector3D[] translateCoordinates(Vector3D[] coordinates, Vector2D direction, bool absolutePosition) { + if (coordinates.Length == 0) return null; + + direction.x = (float)Math.Round(direction.x); + direction.y = (float)Math.Round(direction.y); + + Vector3D[] translatedCoords = new Vector3D[coordinates.Length]; + //move things... if (!absolutePosition) { //...relatively (that's easy) int camAngle = (int)Math.Round(General.Map.VisualCamera.AngleXY * 180 / Math.PI); int sector = (int)(General.ClampAngle(camAngle - 45f) / 90f); direction = direction.GetRotated((float)(sector * Math.PI / 2f)); - for (int i = 0; i < things.Count; i++) { - BaseVisualThing t = things[i] as BaseVisualThing; - t.OnMove(t.Thing.Position + new Vector3D(direction)); - } - } else { //...to specified location preserving relative positioning (that's harder) - if (things.Count == 1) {//just move it there - BaseVisualThing t = things[0] as BaseVisualThing; - t.OnMove(new Vector3D(direction.x, direction.y, t.Thing.Position.z)); - return; - } - - //we need some reference - float minX = things[0].Thing.Position.x; - float maxX = minX; - float minY = things[0].Thing.Position.y; - float maxY = minY; + for (int i = 0; i < coordinates.Length; i++) + translatedCoords[i] = coordinates[i] + new Vector3D(direction); - //get bounding coordinates for selected things - for (int i = 1; i < things.Count; i++) { - if (things[i].Thing.Position.x < minX) - minX = things[i].Thing.Position.x; - else if (things[i].Thing.Position.x > maxX) - maxX = things[i].Thing.Position.x; - - if (things[i].Thing.Position.y < minY) - minY = things[i].Thing.Position.y; - else if (things[i].Thing.Position.y > maxY) - maxY = things[i].Thing.Position.y; - } - - Vector2D selectionCenter = new Vector2D(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); - - //move them - for (int i = 0; i < things.Count; i++) { - BaseVisualThing t = things[i] as BaseVisualThing; - t.OnMove(new Vector3D(direction.x - (selectionCenter.x - t.Thing.Position.x), direction.y - (selectionCenter.y - t.Thing.Position.y), t.Thing.Position.z)); - } + return translatedCoords; } + + //...to specified location preserving relative positioning (that's harder) + if (coordinates.Length == 1) {//just move it there + translatedCoords[0] = new Vector3D(direction.x, direction.y, coordinates[0].z); + return translatedCoords; + } + + //we need some reference + float minX = coordinates[0].x; + float maxX = minX; + float minY = coordinates[0].y; + float maxY = minY; + + //get bounding coordinates for selected things + for (int i = 1; i < coordinates.Length; i++) { + if (coordinates[i].x < minX) + minX = coordinates[i].x; + else if (coordinates[i].x > maxX) + maxX = coordinates[i].x; + + if (coordinates[i].y < minY) + minY = coordinates[i].y; + else if (coordinates[i].y > maxY) + maxY = coordinates[i].y; + } + + Vector2D selectionCenter = new Vector2D(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2); + + //move them + for (int i = 0; i < coordinates.Length; i++) + translatedCoords[i] = new Vector3D((float)Math.Round(direction.x - (selectionCenter.x - coordinates[i].x)), (float)Math.Round(direction.y - (selectionCenter.y - coordinates[i].y)), (float)Math.Round(coordinates[i].z)); + + return translatedCoords; } #endregion @@ -686,6 +716,7 @@ namespace CodeImp.DoomBuilder.GZDoomEditing public override void OnDisengage() { base.OnDisengage(); + copyBuffer.Clear(); //mxd General.Map.Map.Update(); } @@ -941,7 +972,14 @@ namespace CodeImp.DoomBuilder.GZDoomEditing // Undo performed public override void OnUndoEnd() { - base.OnUndoEnd(); + base.OnUndoEnd(); + + //mxd + foreach(KeyValuePair group in visiblesectors){ + if (group.Value is BaseVisualSector) + ((BaseVisualSector)group.Value).Rebuild(); + } + RebuildSelectedObjectsList(); // We can't group with this undo level anymore @@ -952,6 +990,13 @@ namespace CodeImp.DoomBuilder.GZDoomEditing public override void OnRedoEnd() { base.OnRedoEnd(); + + //mxd + foreach (KeyValuePair group in visiblesectors) { + if (group.Value is BaseVisualSector) + ((BaseVisualSector)group.Value).Rebuild(); + } + RebuildSelectedObjectsList(); } @@ -1206,7 +1251,7 @@ namespace CodeImp.DoomBuilder.GZDoomEditing //mxd. Copied from BuilderModes.ThingsMode // This creates a new thing - private Thing InsertThing(Vector2D pos) { + private Thing CreateThing(Vector2D pos) { if (pos.x < General.Map.Config.LeftBoundary || pos.x > General.Map.Config.RightBoundary || pos.y > General.Map.Config.TopBoundary || pos.y < General.Map.Config.BottomBoundary) { General.Interface.DisplayStatus(StatusType.Warning, "Failed to insert thing: outside of map boundaries."); @@ -1588,22 +1633,23 @@ namespace CodeImp.DoomBuilder.GZDoomEditing PostAction(); } - [BeginAction("insertitem", BaseAction = true)] //mxd. now we can actually insert things in Visual modes - public void Insert() + //mxd. now we can actually insert things in Visual modes + [BeginAction("insertitem", BaseAction = true)] + public void InsertThing() { Vector2D hitpos = getHitPosition(); if (!hitpos.IsFinite()) { - General.Interface.DisplayStatus(StatusType.Warning, "Cannot insert item here!"); + General.Interface.DisplayStatus(StatusType.Warning, "Cannot insert thing here!"); return; } ClearSelection(); PreActionNoChange(); - + General.Map.UndoRedo.ClearAllRedos(); General.Map.UndoRedo.CreateUndo("Insert thing"); - Thing t = InsertThing(new Vector2D(hitpos.x, hitpos.y)); + Thing t = CreateThing(new Vector2D(hitpos.x, hitpos.y)); if (t == null) { General.Map.UndoRedo.WithdrawUndo(); @@ -1618,25 +1664,24 @@ namespace CodeImp.DoomBuilder.GZDoomEditing blockmap.AddThing(t); General.Interface.DisplayStatus(StatusType.Action, "Inserted a new thing."); - General.Map.IsChanged = true; - General.Map.ThingsFilter.Update(); PostAction(); } - [BeginAction("deleteitem", BaseAction = true)] //mxd. now we can actually delete things in Visual modes - public void Delete() + //mxd. now we can actually delete things in Visual modes + [BeginAction("deleteitem", BaseAction = true)] + public void DeleteSelectedThings() { List objs = GetSelectedObjects(false, false, true); - if(objs.Count == 0) return; + if (objs.Count == 0) return; + General.Map.UndoRedo.ClearAllRedos(); string rest = objs.Count + " thing" + (objs.Count > 1 ? "s." : "."); - //make undo General.Map.UndoRedo.CreateUndo("Delete " + rest); General.Interface.DisplayStatus(StatusType.Info, "Deleted " + rest); PreActionNoChange(); - foreach(IVisualEventReceiver i in objs) i.OnDelete(); //are they deleted from BlockMap automatically?.. + foreach (IVisualEventReceiver i in objs) i.OnDelete(); //are they deleted from BlockMap automatically?.. // Update cache values General.Map.IsChanged = true; @@ -1644,6 +1689,73 @@ namespace CodeImp.DoomBuilder.GZDoomEditing PostAction(); } + + //mxd + [BeginAction("copyselection", BaseAction = true)] + public void CopySelection() { + List objs = GetSelectedObjects(false, false, true); + if (objs.Count == 0) { + General.Interface.DisplayStatus(StatusType.Warning, "Nothing to copy, select some Things first!"); + return; + } + + copyBuffer.Clear(); + foreach (IVisualEventReceiver i in objs) { + VisualThing vt = i as VisualThing; + if (vt != null) copyBuffer.Add(new ThingCopyData(vt.Thing)); + } + General.Interface.DisplayStatus(StatusType.Info, "Copied " + copyBuffer.Count + " Things"); + } + + //mxd + [BeginAction("cutselection", BaseAction = true)] + public void CutSelection() { + CopySelection(); + DeleteSelectedThings(); + } + + //mxd. We'll just use currently selected objects + [BeginAction("pasteselection", BaseAction = true)] + public void PasteSelection() { + if(copyBuffer.Count == 0){ + General.Interface.DisplayStatus(StatusType.Warning, "Nothing to paste, cut or copy some Things first!"); + return; + } + + Vector2D hitpos = getHitPosition(); + + if (!hitpos.IsFinite()) { + General.Interface.DisplayStatus(StatusType.Warning, "Cannot paste here!"); + return; + } + + General.Map.UndoRedo.ClearAllRedos(); + string rest = copyBuffer.Count + " thing" + (copyBuffer.Count > 1 ? "s." : "."); + General.Map.UndoRedo.CreateUndo("Paste " + rest); + General.Interface.DisplayStatus(StatusType.Info, "Pasted " + rest); + + PreActionNoChange(); + ClearSelection(); + + //get translated positions + Vector3D[] coords = new Vector3D[copyBuffer.Count]; + for (int i = 0; i < copyBuffer.Count; i++ ) + coords[i] = copyBuffer[i].Position; + + Vector3D[] translatedCoords = translateCoordinates(coords, hitpos, true); + + //create things from copyBuffer + for (int i = 0; i < copyBuffer.Count; i++) { + Thing t = CreateThing(new Vector2D()); + if (t != null) { + copyBuffer[i].ApplyTo(t); + t.Move(translatedCoords[i]); + //add thing to blockmap + blockmap.AddThing(t); + } + } + PostAction(); + } #endregion } diff --git a/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs b/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs index 2d69dd97..f5e2d91e 100644 --- a/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs +++ b/Source/Plugins/GZDoomEditing/VisualModes/BaseVisualThing.cs @@ -537,7 +537,6 @@ namespace CodeImp.DoomBuilder.GZDoomEditing } this.Changed = true; - Rebuild(); } #endregion diff --git a/Source/Plugins/TagExplorer/BuilderPlug.cs b/Source/Plugins/TagExplorer/BuilderPlug.cs index 7044b5d2..58aa622e 100644 --- a/Source/Plugins/TagExplorer/BuilderPlug.cs +++ b/Source/Plugins/TagExplorer/BuilderPlug.cs @@ -59,24 +59,24 @@ namespace CodeImp.DoomBuilder.TagExplorer // Geometry pasted public override void OnPasteEnd(PasteOptions options) { if (tagExplorer != null) - tagExplorer.UpdateTree(true); + tagExplorer.UpdateTreeSoon(); } // Undo performed public override void OnUndoEnd() { if (tagExplorer != null) - tagExplorer.UpdateTree(true); + tagExplorer.UpdateTreeSoon(); } // Redo performed public override void OnRedoEnd() { if (tagExplorer != null) - tagExplorer.UpdateTree(true); + tagExplorer.UpdateTreeSoon(); } public override void OnActionEnd(CodeImp.DoomBuilder.Actions.Action action) { if (tagExplorer != null && action.Name == "builder_deleteitem") - tagExplorer.UpdateTree(true); + tagExplorer.UpdateTreeSoon(); } } } diff --git a/Source/Plugins/TagExplorer/Controls/TagExplorer.Designer.cs b/Source/Plugins/TagExplorer/Controls/TagExplorer.Designer.cs index d9366213..48b1b365 100644 --- a/Source/Plugins/TagExplorer/Controls/TagExplorer.Designer.cs +++ b/Source/Plugins/TagExplorer/Controls/TagExplorer.Designer.cs @@ -31,6 +31,7 @@ this.tbSearch = new System.Windows.Forms.TextBox(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.updatetimer = new System.Windows.Forms.Timer(this.components); this.groupBox1.SuspendLayout(); this.SuspendLayout(); // @@ -204,6 +205,11 @@ this.groupBox2.TabIndex = 8; this.groupBox2.TabStop = false; // + // updatetimer + // + this.updatetimer.Interval = 500; + this.updatetimer.Tick += new System.EventHandler(this.updatetimer_Tick); + // // TagExplorer // this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); @@ -237,5 +243,6 @@ private System.Windows.Forms.CheckBox cbSelectOnClick; private System.Windows.Forms.CheckBox cbCommentsOnly; private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.Timer updatetimer; } } diff --git a/Source/Plugins/TagExplorer/Controls/TagExplorer.cs b/Source/Plugins/TagExplorer/Controls/TagExplorer.cs index 71ec716d..fd8bd535 100644 --- a/Source/Plugins/TagExplorer/Controls/TagExplorer.cs +++ b/Source/Plugins/TagExplorer/Controls/TagExplorer.cs @@ -80,14 +80,20 @@ namespace CodeImp.DoomBuilder.TagExplorer public void Setup() { if (this.ParentForm != null) this.ParentForm.Activated += ParentForm_Activated; - UpdateTree(true); + updateTree(true); } public void Terminate() { if (this.ParentForm != null) this.ParentForm.Activated -= ParentForm_Activated; } - public void UpdateTree(bool focusDisplay) { + // This sets the timer to update the list very soon (because we'll have problems if we just call updateTree now) + public void UpdateTreeSoon() { + updatetimer.Stop(); + updatetimer.Start(); + } + + private void updateTree(bool focusDisplay) { bool showTags = (currentDisplayMode == DISPLAY_TAGS || currentDisplayMode == DISPLAY_TAGS_AND_ACTIONS); bool showActions = (currentDisplayMode == DISPLAY_ACTIONS || currentDisplayMode == DISPLAY_TAGS_AND_ACTIONS); bool hasComment = false; @@ -503,12 +509,12 @@ namespace CodeImp.DoomBuilder.TagExplorer //EVENTS private void cbDisplayMode_SelectedIndexChanged(object sender, EventArgs e) { currentDisplayMode = cbDisplayMode.SelectedItem.ToString(); - UpdateTree(true); + updateTree(true); } private void cbSortMode_SelectedIndexChanged(object sender, EventArgs e) { currentSortMode = cbSortMode.SelectedItem.ToString(); - UpdateTree(true); + updateTree(true); } private void treeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { @@ -542,7 +548,7 @@ namespace CodeImp.DoomBuilder.TagExplorer } General.Map.Map.Update(); - UpdateTree(true); + updateTree(true); } else { //select element? @@ -674,7 +680,7 @@ namespace CodeImp.DoomBuilder.TagExplorer //It is called every time a dialog window closes. private void ParentForm_Activated(object sender, EventArgs e){ - UpdateTree(true); + UpdateTreeSoon(); } private void btnClearSearch_Click(object sender, EventArgs e) { @@ -683,11 +689,16 @@ namespace CodeImp.DoomBuilder.TagExplorer } private void tbSearch_TextChanged(object sender, EventArgs e) { - if (tbSearch.Text.Length > 1 || tbSearch.Text.Length == 0) UpdateTree(false); + if (tbSearch.Text.Length > 1 || tbSearch.Text.Length == 0) updateTree(false); } private void cbCommentsOnly_CheckedChanged(object sender, EventArgs e) { - UpdateTree(true); + updateTree(true); + } + + private void updatetimer_Tick(object sender, EventArgs e) { + updatetimer.Stop(); + updateTree(true); } } diff --git a/Source/Plugins/TagExplorer/Controls/TagExplorer.resx b/Source/Plugins/TagExplorer/Controls/TagExplorer.resx index f92c4b48..06f4c670 100644 --- a/Source/Plugins/TagExplorer/Controls/TagExplorer.resx +++ b/Source/Plugins/TagExplorer/Controls/TagExplorer.resx @@ -125,99 +125,99 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABA - FwAAAk1TRnQBSQFMAgEBBgEAAUABAAFAAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA - AwABIAMAAQEBAAEgBgABIBYAAxIBGQMSARkDEgEZNAADEgEZAxIBGQMSARmwAAMwAUwBAgFBAecB/wEC - AS0BoQH/AzABTAMSARksAAMwAUwBswFvASUB/wF5AUkBGwH/AzABTAMSARmsAAEeAV4C/wEWAVgC/wEC - AToBzgH/AQIBLQGhAf8DEgEZLAAB4AGHAS0B/wHZAYYBLAH/AZ4BYwEiAf8BeQFJARsB/wMSARmsAAFy - AZsC/wHoAe8C/wEWAVgC/wECAUEB5wH/AxIBGSwAAf4BtAFqAv8B/QHlAf8B2QGGASwB/wGzAW8BJQH/ - AxIBGawAAzABTAFyAZsC/wEeAV4C/wOJAf8DRgH/AyMBMygAAzABTAH+AbQBagH/AeABhwEtAf8BowGJ - AW8B/wNGAf8DIwEzsAADIwEzA7wB/wOBAf8DRgH/AyMBMwgAAyMBMwMjATMDIwEzGAADIwEzA7wB/wOB - Af8DRgH/AyMBMwgAAyMBMwMjATMDIwEznAADIwEzA80B/wOUAf8DRgH/AyMBMwMwAUwDRgH/A5QB/wMj - ATMcAAMjATMDzQH/A5QB/wNGAf8DIwEzAzABTANGAf8DlAH/AyMBM6AAAyMBMwPNAf8DlAH/A0YB/wNG - Af8DlAH/AyMBMyQAAyMBMwPNAf8DlAH/A0YB/wNGAf8DlAH/AyMBM6gAAyMBMwO4Af8DlAH/A4EB/wMj - ATMsAAMjATMDuAH/A5QB/wOBAf8DIwEzsAADIwEzA88B/wOUAf8DRgH/AyMBMywAAyMBMwPPAf8DlAH/ - A0YB/wMjATOwAAMjATMDwwH/A5QB/wNGAf8DIwEzLAADIwEzA8MB/wOUAf8DRgH/AyMBM7AAAyMBMwO8 - Af8DlAH/A0YB/wMSARkDEgEZAxIBGSQAAyMBMwO8Af8DlAH/A0YB/wMSARkDEgEZAxIBGagAAyMBMwPZ - Af8DtwH/AQIBQQHnAf8BAgEtAaEB/wMwAUwDEgEZJAADIwEzA9kB/wHRAbcBnQH/AbMBbwElAf8BeQFJ - ARsB/wMwAUwDEgEZqAADIwEzAR4BXgL/ARYBWAL/AQIBOgHOAf8BAgEtAaEB/wMSARkoAAMjATMB4AGH - AS0B/wHZAYYBLAH/AZ4BYwEiAf8BeQFJARsB/wMSARmsAAFyAZsC/wHoAe8C/wEWAVgC/wECAUEB5wH/ - AxIBGSwAAf4BtAFqAv8B/QHlAf8B2QGGASwB/wGzAW8BJQH/AxIBGawAAzABTAFyAZsC/wEeAV4C/wMw - AUwwAAMwAUwB/gG0AWoB/wHgAYcBLQH/AzABTJgAAwgBCwMfAS0DJwE7AycBOwMnATsDHgErAxIBGAMS + FwAAAk1TRnQBSQFMAgEBBgEAAUgBAAFIAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA + AwABIAMAAQEBAAEgBgABIBYAAxIBGQMSARkDEgEZNAADEgEZAxIBGQMSARmwAAMwAUwBAQFAAecB/wEB + ASwBoQH/AzABTAMSARksAAMwAUwBswFuASQB/wF4AUgBGgH/AzABTAMSARmsAAEdAV0C/wEVAVcC/wEB + ATkBzgH/AQEBLAGhAf8DEgEZLAAB4AGHASwB/wHZAYYBKwH/AZ4BYgEhAf8BeAFIARoB/wMSARmsAAFx + AZsC/wHoAe8C/wEVAVcC/wEBAUAB5wH/AxIBGSwAAf4BtAFpAv8B/QHlAf8B2QGGASsB/wGzAW4BJAH/ + AxIBGawAAzABTAFxAZsC/wEdAV0C/wOJAf8DRQH/AyMBMygAAzABTAH+AbQBaQH/AeABhwEsAf8BowGJ + AW4B/wNFAf8DIwEzsAADIwEzA7wB/wOBAf8DRQH/AyMBMwgAAyMBMwMjATMDIwEzGAADIwEzA7wB/wOB + Af8DRQH/AyMBMwgAAyMBMwMjATMDIwEznAADIwEzA80B/wOUAf8DRQH/AyMBMwMwAUwDRQH/A5QB/wMj + ATMcAAMjATMDzQH/A5QB/wNFAf8DIwEzAzABTANFAf8DlAH/AyMBM6AAAyMBMwPNAf8DlAH/A0UB/wNF + Af8DlAH/AyMBMyQAAyMBMwPNAf8DlAH/A0UB/wNFAf8DlAH/AyMBM6gAAyMBMwO4Af8DlAH/A4EB/wMj + ATMsAAMjATMDuAH/A5QB/wOBAf8DIwEzsAADIwEzA88B/wOUAf8DRQH/AyMBMywAAyMBMwPPAf8DlAH/ + A0UB/wMjATOwAAMjATMDwwH/A5QB/wNFAf8DIwEzLAADIwEzA8MB/wOUAf8DRQH/AyMBM7AAAyMBMwO8 + Af8DlAH/A0UB/wMSARkDEgEZAxIBGSQAAyMBMwO8Af8DlAH/A0UB/wMSARkDEgEZAxIBGagAAyMBMwPZ + Af8DtwH/AQEBQAHnAf8BAQEsAaEB/wMwAUwDEgEZJAADIwEzA9kB/wHRAbcBnQH/AbMBbgEkAf8BeAFI + ARoB/wMwAUwDEgEZqAADIwEzAR0BXQL/ARUBVwL/AQEBOQHOAf8BAQEsAaEB/wMSARkoAAMjATMB4AGH + ASwB/wHZAYYBKwH/AZ4BYgEhAf8BeAFIARoB/wMSARmsAAFxAZsC/wHoAe8C/wEVAVcC/wEBAUAB5wH/ + AxIBGSwAAf4BtAFpAv8B/QHlAf8B2QGGASsB/wGzAW4BJAH/AxIBGawAAzABTAFxAZsC/wEdAV0C/wMw + AUwwAAMwAUwB/gG0AWkB/wHgAYcBLAH/AzABTJgAAwgBCwMfAS0DJwE7AycBOwMnATsDHgErAxIBGAMS ARggAAMIAQsDHwEtAycBOwMnATsDJwE7Ax4BKwMSARgDEgEYEAADEgEZAxIBGQMSARkgAAMSARkDEgEZ - AxIBGQgAAxIBGQMSARkDEgEZIAADEgEZAxIBGQMSARkUAAM4AVwCXwFkAegBJAEsAYEB/wEeASUBcgH/ - AR0BJAFvAf8BHAEjAWgB/wJaAWAB5AM4AVwDJwE7AxIBGBgAAzgBXAFlAWEBXwHoAYEBVwEkAf8BcgFP - AR4B/wFvAU0BHQH/AWgBSAEcAf8BYQJaAeQDOAFcAycBOwMSARgIAAMwAUwBJgE1AbIB/wEcASkBeAH/ - AzABTAMSARkDEgEZAxIBGQMSARkDEgEZAxIBGQMSARkDMAFMASYBNQGyAf8BHAEpAXgB/wMwAUwDEgEZ - AzABTAGyAW8BJgH/AXgBSQEcAf8DMAFMAxIBGQMSARkDEgEZAxIBGQMSARkDEgEZAxIBGQMwAUwBsgFv - ASYB/wF4AUkBHAH/AzABTAMSARkMAANaAcUBNAFAAbsB/wEyAT4BvAH/ATEBPQG6Af8BMAE8AbcB/wEu - ATgBtAH/ASwBNwGsAf8BJwEvAZQB/wEdASUBcgH/A1oBvQMnATsDEgEYEAADWgHFAbsBgwE0Af8BvAGC - ATIB/wG6AYEBMQH/AbcBgQEwAf8BtAGBAS4B/wGsAXUBLAH/AZQBZwEnAf8BcgFOAR0B/wNaAb0DJwE7 - AxIBGAQAAS8BRwHfAf8BLQFBAdgB/wEiAS4BnQH/ARwBKQF4Af8DPwH/Az8B/wM/Af8DPwH/Az8B/wM/ - Af8DPwH/AS8BRwHfAf8BLQFBAdgB/wEiAS4BnQH/ARwBKQF4Af8DEgEZAd8BhwEvAf8B2AGGAS0B/wGd - AWMBIgH/AXgBSQEcAf8DPwH/Az8B/wM/Af8DPwH/Az8B/wM/Af8DPwH/Ad8BhwEvAf8B2AGGAS0B/wGd - AWMBIgH/AXgBSQEcAf8DEgEZCAADWQG+ATkBRgHTAf8BPgFNAegB/wFCAVAB8wH/AUEBUAHwAf8BQAFQ - Ae8B/wFAAVAB7wH/AT8BTQHtAf8BPAFLAeIB/wEyAT4BuwH/ASIBKQGBAf8DWgG9AycBOwMSARgIAANZ - Ab4B0wGTATkB/wHoAaEBPgH/AfMBqgFCAf8B8AGnAUEB/wHvAaUBQAH/Ae8BpQFAAf8B7QGlAT8B/wHi - AZwBPAH/AbsBggEyAf8BgQFZASIB/wNaAb0DJwE7AxIBGAFrAYEB/QH/AewB5QL/AS0BQQHYAf8BJgE1 - AbIB/wO4Af8DuAH/A7gB/wO4Af8DuAH/A6YB/wOYAf8BawGBAf0B/wHsAeUC/wEtAUEB2AH/ASYBNQGy - Af8DEgEZAf0BtAFrAv8B/QHlAf8B2AGGAS0B/wGyAW8BJgH/A7gB/wO4Af8DuAH/A7gB/wO4Af8DpgH/ - A5gB/wH9AbQBawL/Af0B5QH/AdgBhgEtAf8BsgFvASYB/wMSARkEAAM4AVwBQgFQAeMB/wFBAU8B8QH/ - ATEBPAGzAf8BLwE6AasB/wE/AU0B6gH/AUQBVAH6Af8BQwFSAfYB/wFBAU8B8QH/AUEBUAHwAf8BPgFM - AeoB/wExATwBvgH/ASEBJwGBAf8DOAFcAxIBGAQAAzgBXAHjAaABQgH/AfEBqAFBAf8BswGBATEB/wGr - AXYBLwH/AeoBowE/Af8B+gGuAUQB/wH2AawBQwH/AfEBqAFBAf8B8AGnAUEB/wHqAaMBPgH/Ab4BhAEx - Af8BgQFYASEB/wM4AVwDEgEYAzABTAFrAYEB/QH/AS8BRwHfAf8DMAFMDAADuAH/A04BmQgAAzABTAFr - AYEB/QH/AS8BRwHfAf8DMAFMBAADMAFMAf0BtAFrAf8B3wGHAS8B/wMwAUwMAAO4Af8DTgGZCAADMAFM - Af0BtAFrAf8B3wGHAS8B/wMwAUwIAANfAdsBUQFfAfYB/wFLAVoC/wExATwBsQH/AwIB/wEKAQwBJgH/ - AR8BJgFyAf8BNgFBAcIB/wFBAVEB8QH/AUQBVAH6Af8BQgFSAfQB/wE7AUkB5AH/ASwBNwGsAf8CXAFk - AecDHgErBAADXwHbAfYBsQFRAv8BtAFLAf8BsQF6ATEB/wMCAf8BJgEbAQoB/wFyAU8BHwH/AcIBiAE2 - Af8B8QGnAUEB/wH6Aa4BRAH/AfQBqQFCAf8B5AGeATsB/wGsAXUBLAH/AWQBYgFcAecDHgErBAADmAH/ - A0YB/wMSARkMAAOlAf8DTgGZDAADmAH/Az8B/wMSARkIAAOYAf8DRgH/AxIBGQwAA6UB/wNOAZkMAAOY - Af8DPwH/AxIBGQgAAU4BXAL/AWYBdAL/AVUBYgH7Af8BRwFVAegB/wEKAQsBHAH/AwIB/wMCAf8DAgH/ - AQ4BEQE1Af8BJAEsAYQB/wE4AUQBzgH/AUABTwHvAf8BNgFCAcwB/wElAS4BigH/AycBOwQAAf8BtgFO - Av8BvgFmAf8B+wG3AVUB/wHoAaUBRwH/ARwBFQEKAf8DAgH/AwIB/wMCAf8BNQElAQ4B/wGEAVsBJAH/ - Ac4BkAE4Af8B7wGmAUAB/wHMAY4BNgH/AYoBXwElAf8DJwE7BAADpgH/A0YB/wMSARkgAAOmAf8DPwH/ - AxIBGQgAA6YB/wNGAf8DEgEZIAADpgH/Az8B/wMSARkEAAMjATMBYQFuAv8BdwGEAv8BXwFrAfkB/wFR - AWAB/AH/ASIBKgF3Af8DAgH/AwIB/wMCAf8BCAEJAQ8B/wEbASIBYQH/ASsBNAGdAf8BPgFLAeQB/wE7 - AUoB4QH/ASgBMQGcAf8DJwE7AyMBMwH/Ab0BYQL/AcYBdwH/AfkBugFfAf8B/AG0AVEB/wF3AVMBIgH/ - AwIB/wMCAf8DAgH/AQ8BDAEIAf8BYQFDARsB/wGdAW0BKwH/AeQBoAE+Af8B4QGbATsB/wGcAWsBKAH/ - AycBOwQAA7AB/wNGAf8DTgGZA04BmRQAA04BmQNOAZkDsAH/Az8B/wMSARkIAAOwAf8DRgH/A04BmQNO - AZkUAANOAZkDTgGZA7AB/wM/Af8DEgEZBAADIwEzAWsBeAL/AYEBjAL/AWABbgH4Af8BTwFfAfsB/wE3 - AUMBywH/AgMBBQH/AwIB/wMCAf8BFAEYAUcB/wFEAVMB+QH/AUQBVAH6Af8BQQFQAfAB/wE7AUkB4wH/ - ASkBMwGiAf8DJwE7AyMBMwH/AcEBawL/AcoBgQH/AfgBuAFgAf8B+wGyAU8B/wHLAY4BNwH/AQUBBAED - Af8DAgH/AwIB/wFHATIBFAH/AfkBrgFEAf8B+gGuAUQB/wHwAacBQQH/AeMBnQE7Af8BogFvASkB/wMn - ATsEAAO4Af8DuAH/A7gB/wOlAf8UAAOlAf8DuAH/A7gB/wM/Af8DEgEZCAADuAH/A7gB/wO4Af8DpQH/ - FAADpQH/A7gB/wO4Af8DPwH/AxIBGQgAAWkBeAL/AY4BlwL/AWgBcwH+Af8BUQFgAfUB/wFHAVUB+AH/ - ARMBGAFHAf8BBQEEAQYB/wEgASgBdgH/AwIB/wEXAR0BVgH/AUEBUAHwAf8BQgFQAfMB/wE5AUcB2gH/ - ASsBNAGfAf8DIgExBAAB/wG/AWkC/wHQAY4B/wH+AcEBaAH/AfUBsAFRAf8B+AGvAUcB/wFHATEBEwH/ - AgYBBAH/AXYBUQEgAf8DAgH/AVYBOwEXAf8B8AGnAUEB/wHzAaoBQgH/AdoBlwE5Af8BnwFvASsB/wMi - ATEEAAO4Af8DRgH/AxIBGSAAA7gB/wM/Af8DEgEZCAADuAH/A0YB/wMSARkgAAO4Af8DPwH/AxIBGQgA - A18B2wGWAaAC/wGGAZAC/wFpAXYB+QH/AVQBYgH+Af8BLAE2AZ0B/wESARcBQwH/AUYBVgL/ASoBMwGY - Af8DAgH/ARcBHQFWAf8BQAFPAe0B/wE5AUYB1QH/AlwBZAHqAwwBEAQAA18B2wH/AdIBlgL/AcwBhgH/ - AfkBvAFpAf8B/gG4AVQB/wGdAW0BLAH/AUMBLgESAv8BsgFGAf8BmAFqASoB/wMCAf8BVgE7ARcB/wHt - AaUBQAH/AdUBlAE5Af8BZQFiAVwB6gMMARAEAAO4Af8DRgH/AxIBGSAAA7gB/wM/Af8DEgEZCAADuAH/ - A0YB/wMSARkgAAO4Af8DPwH/AxIBGQgAAzgBXAGYAaIC/wGeAacC/wFyAYEC/wFeAWsB+QH/AUkBVAHk - Af8BOgFFAb4B/wFKAVoB+AH/AUwBWgH9Af8BMwE7AZwB/wMCAf8BMAE6AbEB/wE8AUkB3gH/AzgBXAgA - AzgBXAH/AdMBmAL/AdYBngL/AcUBcgH/AfkBuQFeAf8B5AGlAUkB/wG+AYcBOgH/AfgBrgFKAf8B/QG0 - AUwB/wGcAXABMwH/AwIB/wGxAYABMAH/Ad4BmwE8Af8DOAFcCAADuAH/A0YB/wMSARkMAAOlAf8DTgGZ - DAADuAH/Az8B/wMSARkIAAO4Af8DRgH/AxIBGQwAA6UB/wNOAZkMAAO4Af8DPwH/AxIBGQwAA1UBsgGo - AbEC/wGJAZMC/wF4AYMC/wFvAYAB/AH/AWQBcAH2Af8BXgFqAfYB/wFdAWkB9QH/AVkBZgH7Af8BPAFG - AcIB/wE9AUsB4AH/A1sBxhAAA1UBsgH/AdkBqAL/Ac4BiQL/AcgBeAH/AfwBwQFvAf8B9gG5AWQB/wH2 - AbcBXgH/AfUBtgFdAf8B+wG4AVkB/wHCAYsBPAH/AeABnAE9Af8DWwHGCAADMAFMASYBNQGyAf8BHAEp - AXgB/wMwAUwDEgEZAxIBGQMSARkDuAH/A04BmQMSARkDEgEZAzABTAEmATUBsgH/ARwBKQF4Af8DMAFM - AxIBGQMwAUwBsgFvASYB/wF4AUkBHAH/AzABTAMSARkDEgEZAxIBGQO4Af8DTgGZAxIBGQMSARkDMAFM - AbIBbwEmAf8BeAFJARwB/wMwAUwDEgEZDAADVgGxAZ8BqQL/AZcBogL/AYsBlQL/AXMBggL/AWEBcAH9 - Af8BVwFkAfkB/wFLAVoB9wH/AUQBVAH6Af8DWgG9GAADVgGxAf8B1QGfAv8B0QGXAv8BzwGLAv8BwwFz - Af8B/QG6AWEB/wH5AbYBVwH/AfcBrwFLAf8B+gGtAUQB/wNaAb0MAAEvAUcB3wH/AS0BQQHYAf8BIgEu - AZ0B/wEcASkBeAH/A0YB/wNGAf8DRgH/A7gB/wNGAf8DRgH/A0YB/wEvAUcB3wH/AS0BQQHYAf8BIgEu - AZ0B/wEcASkBeAH/AxIBGQHfAYcBLwH/AdgBhgEtAf8BnQFjASIB/wF4AUkBHAH/A0YB/wNGAf8DRgH/ - A7gB/wNGAf8DRgH/A0YB/wHfAYcBLwH/AdgBhgEtAf8BnQFjASIB/wF4AUkBHAH/AxIBGRAAAzgBXANe - AdgBegGHAv8BgwGNAv8BcwGDAv8BXgFsAv8DXgHYAzgBXCAAAzgBXANeAdgB/wHHAXoC/wHMAYMC/wHC - AXMC/wG8AV4B/wNeAdgDOAFcEAABawGBAf0B/wHsAeUC/wEtAUEB2AH/ASYBNQGyAf8DuAH/A7gB/wO4 - Af8DuAH/A7gB/wOmAf8DmAH/AWsBgQH9Af8B7AHlAv8BLQFBAdgB/wEmATUBsgH/AxIBGQH9AbQBawL/ - Af0B5QH/AdgBhgEtAf8BsgFvASYB/wO4Af8DuAH/A7gB/wO4Af8DuAH/A6YB/wOYAf8B/QG0AWsC/wH9 - AeUB/wHYAYYBLQH/AbIBbwEmAf8DEgEZHAADIwEzAyMBMzgAAyMBMwMjATMcAAMwAUwBawGBAf0B/wEv - AUcB3wH/AzABTBwAAzABTAFrAYEB/QH/AS8BRwHfAf8DMAFMBAADMAFMAf0BtAFrAf8B3wGHAS8B/wMw - AUwcAAMwAUwB/QG0AWsB/wHfAYcBLwH/AzABTAQAAUIBTQE+BwABPgMAASgDAAFAAwABIAMAAQEBAAEB + AxIBGQgAAxIBGQMSARkDEgEZIAADEgEZAxIBGQMSARkUAAM4AVwCXwFjAegBIwErAYEB/wEdASQBcQH/ + ARwBIwFuAf8BGwEiAWcB/wJaAWAB5AM4AVwDJwE7AxIBGBgAAzgBXAFkAWABXwHoAYEBVgEjAf8BcQFO + AR0B/wFuAUwBHAH/AWcBRwEbAf8BYQJaAeQDOAFcAycBOwMSARgIAAMwAUwBJQE0AbIB/wEbASgBdwH/ + AzABTAMSARkDEgEZAxIBGQMSARkDEgEZAxIBGQMSARkDMAFMASUBNAGyAf8BGwEoAXcB/wMwAUwDEgEZ + AzABTAGyAW4BJQH/AXcBSAEbAf8DMAFMAxIBGQMSARkDEgEZAxIBGQMSARkDEgEZAxIBGQMwAUwBsgFu + ASUB/wF3AUgBGwH/AzABTAMSARkMAANaAcUBMwE/AbsB/wExAT0BvAH/ATABPAG6Af8BLwE7AbcB/wEt + ATcBtAH/ASsBNgGsAf8BJgEuAZQB/wEcASQBcQH/A1oBvQMnATsDEgEYEAADWgHFAbsBgwEzAf8BvAGC + ATEB/wG6AYEBMAH/AbcBgQEvAf8BtAGBAS0B/wGsAXQBKwH/AZQBZgEmAf8BcQFNARwB/wNaAb0DJwE7 + AxIBGAQAAS4BRgHfAf8BLAFAAdgB/wEhAS0BnQH/ARsBKAF3Af8DPgH/Az4B/wM+Af8DPgH/Az4B/wM+ + Af8DPgH/AS4BRgHfAf8BLAFAAdgB/wEhAS0BnQH/ARsBKAF3Af8DEgEZAd8BhwEuAf8B2AGGASwB/wGd + AWIBIQH/AXcBSAEbAf8DPgH/Az4B/wM+Af8DPgH/Az4B/wM+Af8DPgH/Ad8BhwEuAf8B2AGGASwB/wGd + AWIBIQH/AXcBSAEbAf8DEgEZCAADWQG+ATgBRQHTAf8BPQFMAegB/wFBAU8B8wH/AUABTwHwAf8BPwFP + Ae8B/wE/AU8B7wH/AT4BTAHtAf8BOwFKAeIB/wExAT0BuwH/ASEBKAGBAf8DWgG9AycBOwMSARgIAANZ + Ab4B0wGTATgB/wHoAaEBPQH/AfMBqgFBAf8B8AGnAUAB/wHvAaUBPwH/Ae8BpQE/Af8B7QGlAT4B/wHi + AZwBOwH/AbsBggExAf8BgQFYASEB/wNaAb0DJwE7AxIBGAFqAYEB/QH/AewB5QL/ASwBQAHYAf8BJQE0 + AbIB/wO4Af8DuAH/A7gB/wO4Af8DuAH/A6YB/wOYAf8BagGBAf0B/wHsAeUC/wEsAUAB2AH/ASUBNAGy + Af8DEgEZAf0BtAFqAv8B/QHlAf8B2AGGASwB/wGyAW4BJQH/A7gB/wO4Af8DuAH/A7gB/wO4Af8DpgH/ + A5gB/wH9AbQBagL/Af0B5QH/AdgBhgEsAf8BsgFuASUB/wMSARkEAAM4AVwBQQFPAeMB/wFAAU4B8QH/ + ATABOwGzAf8BLgE5AasB/wE+AUwB6gH/AUMBUwH6Af8BQgFRAfYB/wFAAU4B8QH/AUABTwHwAf8BPQFL + AeoB/wEwATsBvgH/ASABJgGBAf8DOAFcAxIBGAQAAzgBXAHjAaABQQH/AfEBqAFAAf8BswGBATAB/wGr + AXUBLgH/AeoBowE+Af8B+gGuAUMB/wH2AawBQgH/AfEBqAFAAf8B8AGnAUAB/wHqAaMBPQH/Ab4BhAEw + Af8BgQFXASAB/wM4AVwDEgEYAzABTAFqAYEB/QH/AS4BRgHfAf8DMAFMDAADuAH/A04BmQgAAzABTAFq + AYEB/QH/AS4BRgHfAf8DMAFMBAADMAFMAf0BtAFqAf8B3wGHAS4B/wMwAUwMAAO4Af8DTgGZCAADMAFM + Af0BtAFqAf8B3wGHAS4B/wMwAUwIAANfAdsBUAFeAfYB/wFKAVkC/wEwATsBsQH/AwEB/wEJAQsBJQH/ + AR4BJQFxAf8BNQFAAcIB/wFAAVAB8QH/AUMBUwH6Af8BQQFRAfQB/wE6AUgB5AH/ASsBNgGsAf8CXAFk + AecDHgErBAADXwHbAfYBsQFQAv8BtAFKAf8BsQF5ATAB/wMBAf8BJQEaAQkB/wFxAU4BHgH/AcIBiAE1 + Af8B8QGnAUAB/wH6Aa4BQwH/AfQBqQFBAf8B5AGeAToB/wGsAXQBKwH/AWQBYgFcAecDHgErBAADmAH/ + A0UB/wMSARkMAAOlAf8DTgGZDAADmAH/Az4B/wMSARkIAAOYAf8DRQH/AxIBGQwAA6UB/wNOAZkMAAOY + Af8DPgH/AxIBGQgAAU0BWwL/AWUBcwL/AVQBYQH7Af8BRgFUAegB/wEJAQoBGwH/AwEB/wMBAf8DAQH/ + AQ0BEAE0Af8BIwErAYQB/wE3AUMBzgH/AT8BTgHvAf8BNQFBAcwB/wEkAS0BigH/AycBOwQAAf8BtgFN + Av8BvgFlAf8B+wG3AVQB/wHoAaUBRgH/ARsBFAEJAf8DAQH/AwEB/wMBAf8BNAEkAQ0B/wGEAVoBIwH/ + Ac4BkAE3Af8B7wGmAT8B/wHMAY4BNQH/AYoBXgEkAf8DJwE7BAADpgH/A0UB/wMSARkgAAOmAf8DPgH/ + AxIBGQgAA6YB/wNFAf8DEgEZIAADpgH/Az4B/wMSARkEAAMjATMBYAFtAv8BdgGEAv8BXgFqAfkB/wFQ + AV8B/AH/ASEBKQF2Af8DAQH/AwEB/wMBAf8BBwEIAQ4B/wEaASEBYAH/ASoBMwGdAf8BPQFKAeQB/wE6 + AUkB4QH/AScBMAGcAf8DJwE7AyMBMwH/Ab0BYAL/AcYBdgH/AfkBugFeAf8B/AG0AVAB/wF2AVIBIQH/ + AwEB/wMBAf8DAQH/AQ4BCwEHAf8BYAFCARoB/wGdAWwBKgH/AeQBoAE9Af8B4QGbAToB/wGcAWoBJwH/ + AycBOwQAA7AB/wNFAf8DTgGZA04BmRQAA04BmQNOAZkDsAH/Az4B/wMSARkIAAOwAf8DRQH/A04BmQNO + AZkUAANOAZkDTgGZA7AB/wM+Af8DEgEZBAADIwEzAWoBdwL/AYEBjAL/AV8BbQH4Af8BTgFeAfsB/wE2 + AUIBywH/AgIBBAH/AwEB/wMBAf8BEwEXAUYB/wFDAVIB+QH/AUMBUwH6Af8BQAFPAfAB/wE6AUgB4wH/ + ASgBMgGiAf8DJwE7AyMBMwH/AcEBagL/AcoBgQH/AfgBuAFfAf8B+wGyAU4B/wHLAY4BNgH/AQQBAwEC + Af8DAQH/AwEB/wFGATEBEwH/AfkBrgFDAf8B+gGuAUMB/wHwAacBQAH/AeMBnQE6Af8BogFuASgB/wMn + ATsEAAO4Af8DuAH/A7gB/wOlAf8UAAOlAf8DuAH/A7gB/wM+Af8DEgEZCAADuAH/A7gB/wO4Af8DpQH/ + FAADpQH/A7gB/wO4Af8DPgH/AxIBGQgAAWgBdwL/AY4BlwL/AWcBcgH+Af8BUAFfAfUB/wFGAVQB+AH/ + ARIBFwFGAf8BBAEDAQUB/wEfAScBdQH/AwEB/wEWARwBVQH/AUABTwHwAf8BQQFPAfMB/wE4AUYB2gH/ + ASoBMwGfAf8DIgExBAAB/wG/AWgC/wHQAY4B/wH+AcEBZwH/AfUBsAFQAf8B+AGvAUYB/wFGATABEgH/ + AgUBAwH/AXUBUAEfAf8DAQH/AVUBOgEWAf8B8AGnAUAB/wHzAaoBQQH/AdoBlwE4Af8BnwFuASoB/wMi + ATEEAAO4Af8DRQH/AxIBGSAAA7gB/wM+Af8DEgEZCAADuAH/A0UB/wMSARkgAAO4Af8DPgH/AxIBGQgA + A18B2wGWAaAC/wGGAZAC/wFoAXUB+QH/AVMBYQH+Af8BKwE1AZ0B/wERARYBQgH/AUUBVQL/ASkBMgGY + Af8DAQH/ARYBHAFVAf8BPwFOAe0B/wE4AUUB1QH/AlwBYwHqAwwBEAQAA18B2wH/AdIBlgL/AcwBhgH/ + AfkBvAFoAf8B/gG4AVMB/wGdAWwBKwH/AUIBLQERAv8BsgFFAf8BmAFpASkB/wMBAf8BVQE6ARYB/wHt + AaUBPwH/AdUBlAE4Af8BZAFhAVwB6gMMARAEAAO4Af8DRQH/AxIBGSAAA7gB/wM+Af8DEgEZCAADuAH/ + A0UB/wMSARkgAAO4Af8DPgH/AxIBGQgAAzgBXAGYAaIC/wGeAacC/wFxAYEC/wFdAWoB+QH/AUgBUwHk + Af8BOQFEAb4B/wFJAVkB+AH/AUsBWQH9Af8BMgE6AZwB/wMBAf8BLwE5AbEB/wE7AUgB3gH/AzgBXAgA + AzgBXAH/AdMBmAL/AdYBngL/AcUBcQH/AfkBuQFdAf8B5AGlAUgB/wG+AYcBOQH/AfgBrgFJAf8B/QG0 + AUsB/wGcAW8BMgH/AwEB/wGxAYABLwH/Ad4BmwE7Af8DOAFcCAADuAH/A0UB/wMSARkMAAOlAf8DTgGZ + DAADuAH/Az4B/wMSARkIAAO4Af8DRQH/AxIBGQwAA6UB/wNOAZkMAAO4Af8DPgH/AxIBGQwAA1UBsgGo + AbEC/wGJAZMC/wF3AYMC/wFuAYAB/AH/AWMBbwH2Af8BXQFpAfYB/wFcAWgB9QH/AVgBZQH7Af8BOwFF + AcIB/wE8AUoB4AH/A1sBxhAAA1UBsgH/AdkBqAL/Ac4BiQL/AcgBdwH/AfwBwQFuAf8B9gG5AWMB/wH2 + AbcBXQH/AfUBtgFcAf8B+wG4AVgB/wHCAYsBOwH/AeABnAE8Af8DWwHGCAADMAFMASUBNAGyAf8BGwEo + AXcB/wMwAUwDEgEZAxIBGQMSARkDuAH/A04BmQMSARkDEgEZAzABTAElATQBsgH/ARsBKAF3Af8DMAFM + AxIBGQMwAUwBsgFuASUB/wF3AUgBGwH/AzABTAMSARkDEgEZAxIBGQO4Af8DTgGZAxIBGQMSARkDMAFM + AbIBbgElAf8BdwFIARsB/wMwAUwDEgEZDAADVgGxAZ8BqQL/AZcBogL/AYsBlQL/AXIBggL/AWABbwH9 + Af8BVgFjAfkB/wFKAVkB9wH/AUMBUwH6Af8DWgG9GAADVgGxAf8B1QGfAv8B0QGXAv8BzwGLAv8BwwFy + Af8B/QG6AWAB/wH5AbYBVgH/AfcBrwFKAf8B+gGtAUMB/wNaAb0MAAEuAUYB3wH/ASwBQAHYAf8BIQEt + AZ0B/wEbASgBdwH/A0UB/wNFAf8DRQH/A7gB/wNFAf8DRQH/A0UB/wEuAUYB3wH/ASwBQAHYAf8BIQEt + AZ0B/wEbASgBdwH/AxIBGQHfAYcBLgH/AdgBhgEsAf8BnQFiASEB/wF3AUgBGwH/A0UB/wNFAf8DRQH/ + A7gB/wNFAf8DRQH/A0UB/wHfAYcBLgH/AdgBhgEsAf8BnQFiASEB/wF3AUgBGwH/AxIBGRAAAzgBXANe + AdgBeQGHAv8BgwGNAv8BcgGDAv8BXQFrAv8DXgHYAzgBXCAAAzgBXANeAdgB/wHHAXkC/wHMAYMC/wHC + AXIC/wG8AV0B/wNeAdgDOAFcEAABagGBAf0B/wHsAeUC/wEsAUAB2AH/ASUBNAGyAf8DuAH/A7gB/wO4 + Af8DuAH/A7gB/wOmAf8DmAH/AWoBgQH9Af8B7AHlAv8BLAFAAdgB/wElATQBsgH/AxIBGQH9AbQBagL/ + Af0B5QH/AdgBhgEsAf8BsgFuASUB/wO4Af8DuAH/A7gB/wO4Af8DuAH/A6YB/wOYAf8B/QG0AWoC/wH9 + AeUB/wHYAYYBLAH/AbIBbgElAf8DEgEZHAADIwEzAyMBMzgAAyMBMwMjATMcAAMwAUwBagGBAf0B/wEu + AUYB3wH/AzABTBwAAzABTAFqAYEB/QH/AS4BRgHfAf8DMAFMBAADMAFMAf0BtAFqAf8B3wGHAS4B/wMw + AUwcAAMwAUwB/QG0AWoB/wHfAYcBLgH/AzABTAQAAUIBTQE+BwABPgMAASgDAAFAAwABIAMAAQEBAAEB BgABARYAA/8BAAGPAf8BjwH/BAABBwH/AQcB/wQAAQcB/wEHAf8EAAEHAf8BBwH/BAABAwH/AQMB/wQA AcEBjwHBAY8EAAHgAQ8B4AEPBAAB8AEfAfABHwQAAfgBPwH4AT8EAAH8AR8B/AEfBAAB/gEPAf4BDwQA Af8BAQH/AQEEAAH/AYAB/wGABAAB/wHAAf8BwAQAAf8B4AH/AeAEAAH/AeEB/wHhBAAB+AEHAfgBBwGP @@ -230,4 +230,10 @@ 122, 17 + + 122, 17 + + + 219, 17 + \ No newline at end of file