From 6928c2f898f881edafa49f298eef1df1e4a4b503 Mon Sep 17 00:00:00 2001 From: Alison Watson Date: Sun, 10 Jul 2022 07:24:30 -0600 Subject: [PATCH] + add aesthetic fixes for small hud font to make it consistent with the big hud font + add option to add a projectile to timon's axe (splits mana use) + add option to speed up sapphire wand + make pickup flashes bright + make sapphire wand's trail look more like the explosion + add option to display a detailed quest log on the automap + make ettins and centaurs not play a hit sound when missing + add an option to set the behaviour of centaurs to make them more vulnerable + add an option to make players shatter frozen enemies on touch + add an option to change the sapphire wand's damage function + make sapphire wand's projectile appear at the proper height + add an option to change the frost spell's damage function + add an option to make bloodscourge's projectiles foil invulnerability --- .gitignore | 1 + cvarinfo.txt | 8 +++ graphics/SMALLIN1.png | Bin 0 -> 876 bytes graphics/SMALLIN4.png | Bin 0 -> 883 bytes graphics/SMALLIN7.png | Bin 0 -> 882 bytes language.txt | 61 +++++++++++++++++ mapinfo.txt | 4 ++ menudef.txt | 35 ++++++++++ vhtzs/events.zsc | 69 +++++++++++++++++++ vhtzs/fnplayer.zsc | 56 +++++++++++++++ vhtzs/fx.zsc | 12 ++++ vhtzs/monsters.zsc | 49 +++++++++++++ vhtzs/player.zsc | 38 +++++++++++ vhtzs/projectiles.zsc | 67 ++++++++++++++++++ vhtzs/quest.zsc | 155 ++++++++++++++++++++++++++++++++++++++++++ vhtzs/weapons.zsc | 49 +++++++++++++ zscript.zsc | 10 +++ 17 files changed, 614 insertions(+) create mode 100644 .gitignore create mode 100644 cvarinfo.txt create mode 100644 graphics/SMALLIN1.png create mode 100644 graphics/SMALLIN4.png create mode 100644 graphics/SMALLIN7.png create mode 100644 language.txt create mode 100644 mapinfo.txt create mode 100644 menudef.txt create mode 100644 vhtzs/events.zsc create mode 100644 vhtzs/fnplayer.zsc create mode 100644 vhtzs/fx.zsc create mode 100644 vhtzs/monsters.zsc create mode 100644 vhtzs/player.zsc create mode 100644 vhtzs/projectiles.zsc create mode 100644 vhtzs/quest.zsc create mode 100644 vhtzs/weapons.zsc create mode 100644 zscript.zsc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..396b2b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +gzdoom-crash.log diff --git a/cvarinfo.txt b/cvarinfo.txt new file mode 100644 index 0000000..b36ac63 --- /dev/null +++ b/cvarinfo.txt @@ -0,0 +1,8 @@ +server bool vht_faxe_projectile = true; +server int vht_mwand_damagefunc = 0; +server int vht_mwand_speed = 1; +server int vht_mfrost_damagefunc = 1; +server bool vht_mbloodscourge_foilinvul = false; +server bool vht_player_touchshatter = true; +server bool vht_player_questlog = true; +server int vht_monster_centaur = 2; diff --git a/graphics/SMALLIN1.png b/graphics/SMALLIN1.png new file mode 100644 index 0000000000000000000000000000000000000000..444c859c7a17ccf740c60d6f05b0d45167b6eb22 GIT binary patch literal 876 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 literal 0 HcmV?d00001 diff --git a/language.txt b/language.txt new file mode 100644 index 0000000..6438a36 --- /dev/null +++ b/language.txt @@ -0,0 +1,61 @@ +[en default] +OPTMNU_VHT = "Vanilla Hexen Tweaks"; + +OB_VHTFWEAPAXE = "%o rode the axe-lightning from %k."; + +VHT_FAXE_PROJECTILE = "Timon's Axe Also Fires a Projectile"; +VHT_MWAND_DAMAGEFUNC = "Wand Damage Function"; +VHT_MWAND_SPEED = "Wand Speed Increase"; +VHT_MFROST_DAMAGEFUNC = "Frost Shards Damage Function"; +VHT_MBLOODSCOURGE_FOILINVUL = "Bloodscourge Foils Invulnerability"; +VHT_PLAYER_TOUCHSHATTER = "Shatter Frozen Enemies On Touch"; +VHT_PLAYER_QUESTLOG = "Enable Quest Log"; +VHT_MONSTER_CENTAUR = "Centaur Behaviour"; + +VHT_OPT_CONSTANT = "Constant"; +VHT_OPT_MODIFIED = "Modified"; +VHT_OPT_VANILLA = "Vanilla"; +VHT_OPT_NOREFLECT = "No Projectile Reflection"; +VHT_OPT_ANGLED = "No Reflection, Back is Vulnerable"; + +VHT_QST_1_0 = + "@ must find the emerald key\n" + "and get through the stronghold.\n" + "@' note says, 'it lays beyond\n" + "a stained warrior.'"; +VHT_QST_1_1 = + "the chapel stands tall and\n" + "proud in the name of sin,\n" + "but its doors stay locked.\n" + "@ must find the silver key."; +VHT_QST_1_2 = + "@ need to ring the chapel's bell,\n" + "to call for the doom about\n" + "to befall cronos, and to build\n" + "a path forward."; +VHT_QST_1_3 = + "now, @ must find @' way to\n" + "the first step -- the seven portals.\n" + "\cr@ will not be able to return."; + +VHT_QST_2_0 = + "the first step on @' journey\n" + "shall be to get through the guardians\n" + "which seal the realm beyond.\n" + "\n" + "the portals once meant to defend cronos\n" + "from invasion now merely keep @= from\n" + "delivering justice unto it."; +VHT_QST_2_1 = + "with the guardian of ice opened,\n" + "two more paths become available to @=:\n" + "- guardian of fire: %i/1\n" + "- guardian of steel: %i/2"; + +VHT_QST_3_0 = + "all that seems to be left here is\n" + "a switch, and some portals... perhaps\n" + "all @ need is to pass through here for now."; +VHT_QST_3_1 = + "there doesn't seem to be anything here\n" + "for @= right now."; diff --git a/mapinfo.txt b/mapinfo.txt new file mode 100644 index 0000000..837470e --- /dev/null +++ b/mapinfo.txt @@ -0,0 +1,4 @@ +GameInfo { + PlayerClasses = "VhtFighterPlayer", "VhtClericPlayer", "VhtMagePlayer" + AddEventHandlers = "VhtEvents" +} diff --git a/menudef.txt b/menudef.txt new file mode 100644 index 0000000..968cbcf --- /dev/null +++ b/menudef.txt @@ -0,0 +1,35 @@ +OptionValue "VhtDamageFunc" { + 0, "$VHT_OPT_VANILLA" + 1, "$VHT_OPT_MODIFIED" + 2, "$VHT_OPT_CONSTANT" +} + +OptionValue "VhtOnOff" { + 0, "$VHT_OPT_VANILLA" + 1, "$VHT_OPT_MODIFIED" +} + +OptionValue "VhtCentaurBehaviour" { + 0, "$VHT_OPT_VANILLA" + 1, "$VHT_OPT_NOREFLECT" + 2, "$VHT_OPT_ANGLED" +} + +AddOptionMenu "OptionsMenu" { + SubMenu "$OPTMNU_VHT", "VhtMenu" +} + +OptionMenu "VhtMenu" { + Title "$OPTMNU_VHT" + StaticText "Weapons" + Option "$VHT_FAXE_PROJECTILE", "vht_faxe_projectile", "OnOff" + Option "$VHT_MWAND_DAMAGEFUNC", "vht_mwand_damagefunc", "VhtDamageFunc" + ScaleSlider "$VHT_MWAND_SPEED", "vht_mwand_speed", 0, 6, 1, "$VHT_OPT_VANILLA" + Option "$VHT_MFROST_DAMAGEFUNC", "vht_mfrost_damagefunc", "VhtDamageFunc" + Option "$VHT_MBLOODSCOURGE_FOILINVUL", "vht_mbloodscourge_foilinvul", "OnOff" + StaticText "Players" + Option "$VHT_PLAYER_TOUCHSHATTER", "vht_player_touchshatter", "OnOff" + Option "$VHT_PLAYER_QUESTLOG", "vht_player_questlog", "OnOff" + StaticText "Monsters" + Option "$VHT_MONSTER_CENTAUR", "vht_monster_centaur", "VhtCentaurBehaviour" +} diff --git a/vhtzs/events.zsc b/vhtzs/events.zsc new file mode 100644 index 0000000..d71b494 --- /dev/null +++ b/vhtzs/events.zsc @@ -0,0 +1,69 @@ +class VhtEvents : StaticEventHandler { + bool m_useQuestLog; + string m_questLog; + VhtFnPlayerInv m_fGetQuests; + VhtQuestHolder vhtGetQuests() const { + return m_fGetQuests.vhtRun() ? VhtQuestHolder(m_fGetQuests.m_result) : null; + } + override void playerEntered(PlayerEvent e) { + let p = players[e.playerNumber].mo; + if(p && !p.findInventory("VhtQuestHolder")) { + p.giveInventoryType("VhtQuestHolder"); + } + if(!m_fGetQuests) { + m_fGetQuests = new("VhtFnPlayerInvExist").vhtInit("VhtQuestHolder"); + } + let qh = vhtGetQuests(); + if(!e.isReturn && m_fGetQuests.m_player == e.playerNumber) { + qh.vhtAddQuest("VhtQuest" .. level.levelNum); + } + } + override void playerDisconnected(PlayerEvent e) { + let p = players[e.playerNumber].mo; + if(p) { + let qh_r = VhtQuestHolder(p.findInventory("VhtQuestHolder")); + if(qh_r) { + p.removeInventory(qh_r); + let qh_l = vhtGetQuests(); + if(qh_l) { + qh_r.m_quests.move(qh_l.m_quests); + } + } + } + } + override void worldTick() { + m_useQuestLog = vht_player_questlog; + let qh = vhtGetQuests(); + if(!qh) { + return; + } + for(int i = 0, j = qh.m_quests.size(); i < j; ++i) { + if(qh.m_quests[i]) { + qh.m_quests[i].vhtTick(); + } + } + if(m_useQuestLog) { + m_questLog = ""; + for(int i = 0, j = qh.m_quests.size(); i < j; ++i) { + if(qh.m_quests[i]) { + m_questLog.appendFormat( + "\cu- \cn%s\c-\n%s\n\n", + StringTable.localize( + qh.m_quests[i].vhtLevelInfo().levelName, + false + ), + qh.m_quests[i].vhtDescribe() + ); + } + } + } + } + override void renderUnderlay(RenderEvent e) { + if(automapActive && m_useQuestLog) { + double x = Screen.getWidth() / 320.0; + double y = Screen.getHeight() / 8.0; + Screen.drawText(smallfont, Font.CR_UNTRANSLATED, x+x, y+x, m_questLog, DTA_CLEANNOMOVE_1,true, DTA_ALPHA,0.406, DTA_FILLCOLOR,0); + Screen.drawText(smallfont, Font.CR_UNTRANSLATED, x, y, m_questLog, DTA_CLEANNOMOVE_1,true); + } + } +} diff --git a/vhtzs/fnplayer.zsc b/vhtzs/fnplayer.zsc new file mode 100644 index 0000000..5091d24 --- /dev/null +++ b/vhtzs/fnplayer.zsc @@ -0,0 +1,56 @@ +class VhtFnPlayer abstract { + int m_player; + virtual bool vhtCall(PlayerInfo p) {return false;} + bool vhtRun() { + for(int i = 0; i < MAXPLAYERS; ++i) { + if(playerInGame[i] && vhtCall(players[i])) { + m_player = i; + return true; + } + } + return false; + } +} + +class VhtFnPlayerInSector : VhtFnPlayer { + int m_idx; + VhtFnPlayer vhtInit(int idx) { + m_idx = idx; + return self; + } + override bool vhtCall(PlayerInfo p) { + return p.mo.curSector.index() == m_idx; + } +} + +class VhtFnPlayerInv : VhtFnPlayer abstract { + class m_which; + Inventory m_result; + override bool vhtCall(PlayerInfo p) { + let inv = p.mo.findInventory(m_which); + if(inv) { + m_result = inv; + return true; + } + return false; + } +} + +class VhtFnPlayerInvExist : VhtFnPlayerInv { + VhtFnPlayerInv vhtInit(class which) { + m_which = which; + return self; + } +} + +class VhtFnPlayerInvAmount : VhtFnPlayerInv { + int m_amount; + VhtFnPlayerInv vhtInit(class which, int amount) { + m_which = which; + m_amount = amount; + return self; + } + override bool vhtCall(PlayerInfo p) { + return super.vhtCall(p) && m_result.amount >= m_amount; + } +} diff --git a/vhtzs/fx.zsc b/vhtzs/fx.zsc new file mode 100644 index 0000000..8793523 --- /dev/null +++ b/vhtzs/fx.zsc @@ -0,0 +1,12 @@ +class VhtPickupFlash : PickupFlash replaces PickupFlash { + default { + +Bright; + } +} + +class VhtMageWandSmoke : MageWandSmoke replaces MageWandSmoke { + default { + RenderStyle "Add"; + Translation "156:163=217:223"; + } +} diff --git a/vhtzs/monsters.zsc b/vhtzs/monsters.zsc new file mode 100644 index 0000000..f0c5c5c --- /dev/null +++ b/vhtzs/monsters.zsc @@ -0,0 +1,49 @@ +class VhtEttin : Ettin replaces Ettin { + default { + AttackSound ""; + } + states { + Melee: + ETTN EF 6 a_faceTarget; + ETTN G 8 a_customMeleeAttack(random[EttinAttack](1, 8) * 2, "EttinAttack", "FighterPunchMiss"); + goto See; + } +} + +class VhtCentaur : Centaur replaces Centaur { + default { + AttackSound ""; + } + void vhtSetReflectiveInvulnerable() { + bInvulnerable = true; + if(vht_monster_centaur != 0) + bReflective = true; + } + void vhtUnsetReflectiveInvulnerable() { + bInvulnerable = false; + if(vht_monster_centaur != 0) + bReflective = false; + } + override int damageMobj(Actor inflictor, Actor source, int damage, Name mod, int flags, double hitAngle) { + if(vht_monster_centaur == 2 && bInvulnerable) { + double angleToSrc = inflictor ? angleTo(inflictor) - angle : hitAngle; + if(angleToSrc < -90 || angleToSrc > 90) { + flags |= DMG_FOILINVUL; + } + } + return super.damageMobj(inflictor, source, damage, mod, flags, angle); + } + states { + Pain: + CENT G 6 a_pain; + CENT G 6 vhtSetReflectiveInvulnerable; + CENT EEE 15 a_centaurDefend; + CENT E 1 vhtUnsetReflectiveInvulnerable; + goto See; + Melee: + CENT H 5 a_faceTarget; + CENT I 4 a_faceTarget; + CENT J 7 a_customMeleeAttack(random[CentaurAttack](3, 9), "CentaurAttack", "FighterHammerMiss"); + goto See; + } +} diff --git a/vhtzs/player.zsc b/vhtzs/player.zsc new file mode 100644 index 0000000..8528d7b --- /dev/null +++ b/vhtzs/player.zsc @@ -0,0 +1,38 @@ +mixin class VhtPlayer { + bool m_touchShatter; + override bool canCollideWith(Actor other, bool passive) { + if(m_touchShatter && other.bIceCorpse && other.distance3D(other) <= other.radius + 4) { + other.damageMobj(self, self, 1, 'Melee'); + } + return true; + } + override void tick() { + m_touchShatter = vht_player_touchshatter; + super.tick(); + } +} + +class VhtFighterPlayer : FighterPlayer { + mixin VhtPlayer; + default { + Player.WeaponSlot 1, "FWeapFist"; + Player.WeaponSlot 2, "VhtFWeapAxe"; + Player.WeaponSlot 3, "FWeapHammer"; + Player.WeaponSlot 4, "FWeapQuietus"; + } +} + +class VhtClericPlayer : ClericPlayer { + mixin VhtPlayer; +} + +class VhtMagePlayer : MagePlayer { + mixin VhtPlayer; + default { + Player.StartItem "VhtMWeapWand"; + Player.WeaponSlot 1, "VhtMWeapWand"; + Player.WeaponSlot 2, "MWeapFrost"; + Player.WeaponSlot 3, "MWeapLightning"; + Player.WeaponSlot 4, "MWeapBloodscourge"; + } +} diff --git a/vhtzs/projectiles.zsc b/vhtzs/projectiles.zsc new file mode 100644 index 0000000..cebbcdb --- /dev/null +++ b/vhtzs/projectiles.zsc @@ -0,0 +1,67 @@ +class VhtMageWandMissile : MageWandMissile replaces MageWandMissile { + default { + DamageFunction vhtDamageFunc(); + } + override void beginPlay() { + super.beginPlay(); + spriteOffset = (0, 8); + } + int vhtDamageFunc() { + switch(vht_mwand_damagefunc) { + case 0: return 2 * random(1, 8); /* vanilla (2d8) */ + case 1: return 4 * random(1, 4); /* modified (4d4) */ + case 2: return 16; /* constant */ + } + return 0; + } +} + +class VhtFrostMissile : FrostMissile replaces FrostMissile { + default { + DamageFunction vhtDamageFunc(); + } + int vhtDamageFunc() { + switch(vht_mfrost_damagefunc) { + case 0: return 1 * random(1, 8); /* vanilla (1d8) */ + case 1: return 4 * random(1, 3); /* modified (4d3) */ + case 2: return 8; /* constant */ + } + return 0; + } +} + +class VhtBloodscourgeShot : MageStaffFx2 replaces MageStaffFx2 { + override void beginPlay() { + super.beginPlay(); + if(vht_mbloodscourge_foilinvul) { + bFoilInvul = true; + } + } +} + +class VhtAxeMissile : Actor { + default { + Speed 25; + Radius 13; + Height 8; + Damage 2; + DamageType "Melee"; + Projectile; + DeathSound "MageLightningZap"; + Obituary "$OB_VHTFWEAPAXE"; + Translation "146:163=146:160", "217:223=155:163"; + } + states { + Spawn: + SHRD A 2 bright; + SHRD ABC 3 bright; + loop; + Death: + MWND E 4 bright; + MWND F 3 bright; + MWND G 4 bright; + MWND H 3 bright; + MWND I 4 bright; + stop; + } +} diff --git a/vhtzs/quest.zsc b/vhtzs/quest.zsc new file mode 100644 index 0000000..25fa4f3 --- /dev/null +++ b/vhtzs/quest.zsc @@ -0,0 +1,155 @@ +class VhtQuest abstract play { + int m_mapNum, m_step, m_flags; + VhtHubQuest m_hubQuest; + virtual VhtQuest vhtInit(VhtHubQuest hubQuest) { + m_mapNum = level.levelNum; + m_hubQuest = hubQuest; + return self; + } + virtual void vhtTick() {} + virtual void vhtTravelled() {} + virtual void vhtPreTravelled() {} + virtual string vhtDescribe() { + let s = StringTable.localize("$VHT_QST_" .. m_mapNum .. "_" .. m_step); + s.replace("@=", multiplayer ? "us" : "me"); + s.replace("@'", multiplayer ? "our" : "my"); + s.replace("@", multiplayer ? "we" : "i"); + return s; + } + LevelInfo vhtLevelInfo() const { + return LevelInfo.findLevelByNum(m_mapNum); + } +} + +class VhtHubQuest : VhtQuest abstract { + override VhtQuest vhtInit(VhtHubQuest hubQuest) { + super.vhtInit(self); + vhtInitHub(); + return self; + } + virtual void vhtInitHub() {} +} + +// quest 1: winnowing hall +class VhtQuest1 : VhtHubQuest { + VhtFnPlayer m_fStep0; // quest 1 step 0: walk into courtyard + VhtFnPlayer m_fStep1; // quest 1 step 1: pick up silver key + Line m_lStep2; // quest 1 step 2: ring the bell + override void vhtInitHub() { + m_fStep0 = new("VhtFnPlayerInSector") .vhtInit(115); + m_fStep1 = new("VhtFnPlayerInvAmount").vhtInit("KeySilver", 1); + m_lStep2 = level.lines[level.createLineIdIterator(2).next()]; + } + // same hub, but cannot return + override void vhtTravelled() { + destroy(); + } + override void vhtTick() { + switch(m_step) { + case 0: if(m_fStep0.vhtRun()) {m_step = 1;} break; + case 1: if(m_fStep1.vhtRun()) {m_step = 2;} // --> + case 2: if(!(m_lStep2.flags & Line.ML_BLOCKING)) {m_step = 3;} break; + } + } +} + +// quest 2: seven portals +class VhtQuest2 : VhtHubQuest { + // quest 2 step 1: 3 switches pulled in first puzzle + int m_guardianOfFire, m_guardianOfSteel; + override void vhtTravelled() { + // quest 2 step 0: return from guardian of ice + if(m_step == 0 && level.levelNum == m_mapNum) { + ++m_step; + } + } + override string vhtDescribe() { + let s = super.vhtDescribe(); + if(m_step == 1) { + s = string.format(s, m_guardianOfFire, m_guardianOfSteel); + } + return s; + } +} + +// quest 3: guardian of ice +class VhtQuest3 : VhtQuest { + override void vhtPreTravelled() { + // quest 3 step 0: exit to hub + if(m_step == 0) { + ++m_step; + } + } +} + +// quest 4: guardian of fire +class VhtQuest4 : VhtQuest {} + +// quest 5: guardian of steel +class VhtQuest5 : VhtQuest {} + +// quest 6: bright crucible +class VhtQuest6 : VhtQuest {} + +class VhtQuest13 : VhtHubQuest {} +class VhtQuest8 : VhtQuest {} +class VhtQuest9 : VhtQuest {} +class VhtQuest10 : VhtQuest {} +class VhtQuest12 : VhtQuest {} +class VhtQuest11 : VhtQuest {} + +class VhtQuest27 : VhtHubQuest {} +class VhtQuest32 : VhtQuest {} +class VhtQuest33 : VhtQuest {} +class VhtQuest34 : VhtQuest {} +class VhtQuest28 : VhtQuest {} +class VhtQuest30 : VhtQuest {} +class VhtQuest31 : VhtQuest {} + +class VhtQuest22 : VhtHubQuest {} +class VhtQuest21 : VhtQuest {} +class VhtQuest23 : VhtQuest {} +class VhtQuest24 : VhtQuest {} +class VhtQuest25 : VhtQuest {} +class VhtQuest26 : VhtQuest {} + +class VhtQuest35 : VhtHubQuest {} +class VhtQuest36 : VhtQuest {} +class VhtQuest37 : VhtQuest {} +class VhtQuest38 : VhtQuest {} +class VhtQuest39 : VhtQuest {} + +class VhtQuest40 : VhtHubQuest {} + +class VhtQuestHolder : Inventory { + default { + Inventory.InterHubAmount 0; + +Inventory.UnDroppable; + +Inventory.UnClearable; + } + VhtHubQuest m_hubQuest; + array m_quests; + void vhtAddQuest(class ty) { + if(ty) { + let qst = VhtQuest(new(ty)).vhtInit(m_hubQuest); + if(qst is "VhtHubQuest") { + m_hubQuest = VhtHubQuest(qst); + } + m_quests.push(qst); + } + } + override void depleteOrDestroy() { + m_hubQuest = null; + m_quests.clear(); + } + override void preTravelled() { + for(int i = 0, j = m_quests.size(); i < j; ++i) { + m_quests[i].vhtPreTravelled(); + } + } + override void travelled() { + for(int i = 0, j = m_quests.size(); i < j; ++i) { + m_quests[i].vhtTravelled(); + } + } +} diff --git a/vhtzs/weapons.zsc b/vhtzs/weapons.zsc new file mode 100644 index 0000000..ce39d2c --- /dev/null +++ b/vhtzs/weapons.zsc @@ -0,0 +1,49 @@ +class VhtFWeapAxe : FWeapAxe replaces FWeapAxe { + override void beginPlay() { + super.beginPlay(); + if(vht_faxe_projectile) { + ammoUse1 = 1; + } + } + action void vhtFAxeAttack() { + if(!player) return; + if(vht_faxe_projectile) { + let wep = player.readyWeapon; + if(wep.ammo1 && wep.ammo1.amount > 0) { + a_fireProjectile("VhtAxeMissile"); + } + } + a_fAxeAttack(); + } + states { + FireGlow: + FAXE N 4 offset(15, 32); + FAXE O 3 offset(15, 32); + FAXE P 2 offset(15, 32); + FAXE P 1 offset(-5, 70) vhtFAxeAttack; + FAXE P 2 offset(-25, 90); + FAXE Q 1 offset(15, 32); + FAXE Q 2 offset(10, 54); + FAXE Q 7 offset(10, 150); + FAXE A 1 offset(0, 60) a_reFire; + FAXE A 1 offset(0, 52); + FAXE A 1 offset(0, 44); + FAXE A 1 offset(0, 36); + FAXE A 1; + goto ReadyGlow; + } +} + +class VhtMWeapWand : MWeapWand replaces MWeapWand { + states { + Fire: + MWND A 0 a_setTics(6 - clamp(vht_mwand_speed, 0, 6)); + MWND B 0 bright offset(0, 48) { + a_setTics(6 - clamp(vht_mwand_speed, 0, 6)); + a_fireProjectile("MageWandMissile"); + } + MWND A 3 offset(0, 40); + MWND A 3 offset(0, 36) a_reFire; + goto Ready; + } +} diff --git a/zscript.zsc b/zscript.zsc new file mode 100644 index 0000000..7da235e --- /dev/null +++ b/zscript.zsc @@ -0,0 +1,10 @@ +version "4.8" + +#include "vhtzs/fnplayer.zsc" +#include "vhtzs/fx.zsc" +#include "vhtzs/projectiles.zsc" +#include "vhtzs/weapons.zsc" +#include "vhtzs/player.zsc" +#include "vhtzs/monsters.zsc" +#include "vhtzs/quest.zsc" +#include "vhtzs/events.zsc"