diff --git a/README.md b/README.md index 9c9e3f294..1cdcaace2 100644 --- a/README.md +++ b/README.md @@ -347,32 +347,30 @@ have a spare. Most powerups can be toggled, unless specified otherwise. ### Health Nugget, replaces Health Bonus, Crystal Vial -Health nuggets increase health by 1% up to a cap of 200. +Health nuggets increase health by 5% up to a cap of 200. ### Health Tetrahedron, replaces Stimpak, Quartz Flask Health tetrahedrons provide a 15% health boost up to a cap of 100. -### Health Cube, replaces Medkit, Quartz Flask +### Health Cube, replaces Medkit, Mystic Urn (Hexen), Quartz Flask (Heretic) Health cubes provide a 30% health boost up to a cap of 100. -### Refresher, replaces Soulsphere, Mystic Urn +### Refresher, replaces Soulsphere, Mystic Urn (Heretic), Mystic Ambit Incant This artifact provides a massive boost of health to 500, plus a regeneration -effect that heals 10% every 5 seconds, for up to 60 seconds, thus it doubles +effect that heals 5% every 2 seconds, for up to 60 seconds, thus it doubles as a powerup. Like other health items, the Refresher is auto-activated if you're about to die, though it sometimes won't be enough to save you. ### Armor Nugget, replaces Armor Bonus, Timebomb of the Ancients, Disc of Repulsion Cumulative armor items that can increase resistance to all damage by as much -as you can find. Each nugget adds 1% to the total. Above 100%, damage gets +as you can find. Each nugget adds 5% to the total. Above 100%, damage gets turned into additional health (up to the standard 100% cap). The upper cap for these is 200%. -Protection decreases by a 10% of absorbed damage. - ### Blast Suit, replaces Green Armor, Silver Shield, Mesh Armor The blast suit is a nice little light armor which provides a 75% reduction to @@ -428,7 +426,7 @@ Just be careful with height when the effect is about to run out. This thing makes you impervious to pretty much everything excluding the Ynykron Artifact. -You're fucking invincible for a total of 90 seconds. +You're fucking invincible for a total of 30 seconds. ### Hammerspace Embiggener, replaces Backpack, Bag of Holding, Porkalator @@ -441,7 +439,7 @@ Separated into four tiers, they work pretty much like the ammo cubes in Doom Tournament, except they try to "balance" the total ammo given. The max tier fabricator is the only one capable of producing ammo for superweapons. -### Lamp, replaces Lite-Amp, Torch, Mystic Ambit Incant +### Lamp, replaces Lite-Amp, Torch A floating lamp companion, may attract moths. The moths will disperse and attack nearby enemies when toggling it off. diff --git a/gldefs.pickups b/gldefs.pickups index dc207b4c1..301aee663 100644 --- a/gldefs.pickups +++ b/gldefs.pickups @@ -45,6 +45,14 @@ Brightmap Texture "models/Ragekit.png" { Map "models/Ragekit_bright.png" } +Brightmap Texture "models/Omnisight.png" +{ + Map "models/Omnisight_bright.png" +} +Brightmap Texture "models/Lamp.png" +{ + Map "models/Lamp_bright.png" +} PointLight ARMORNUGGETLIGHT { @@ -144,3 +152,17 @@ Object Ragekit { Frame "XZW1" { light "RAGELIGHT" } } + +FlickerLight2 LAMPLIGHT +{ + Color 0.6 1.0 0.7 + Size 240 + SecondarySize 248 + Interval 0.1 + Offset 0 8 0 + Attenuate 1 +} +Object CompanionLamp +{ + Frame "XZW1B" { light "LAMPLIGHT" } +} diff --git a/graphics/KBase/Drawing.png b/graphics/KBase/Drawing.png new file mode 100644 index 000000000..63507fddd Binary files /dev/null and b/graphics/KBase/Drawing.png differ diff --git a/language.txt b/language.txt index 06198051c..303a8d069 100644 --- a/language.txt +++ b/language.txt @@ -101,6 +101,7 @@ SWWM_MSGSENT = "You sent %dx %s to %s."; SWWM_MSGRECV = "%s sent you %dx %s."; SWWM_CHATTAB = "Chat Log"; SWWM_SECRETTAB = "Secret"; +SWWM_TODEMO = "To Demo-chan, from Saya and Ibuki \cg♥\c-"; SWWM_MAINCONTROLS = "PgUp/PgDn: Switch Tab | Arrows: Navigate | "; SWWM_INVCONTROLS = "Enter: Use | Backspace: Drop | "; SWWM_STRCONTROLS = "Enter: Buy | "; @@ -815,11 +816,13 @@ T_TETRAHEALTH = "Health Tetrahedron"; T_CUBEHEALTH = "Health Cube"; T_INVINCIBALL = "Fuckin' Invinciball"; T_LAMP = "Lämp"; +I_LAMP = "Companion Lamp"; T_MOTH = "Moth"; T_MASHIRO = "White Moth"; T_NUGGETH = "Health Nugget"; T_NUGGETA = "Armor Nugget"; T_OMNISIGHT = "Omnisight"; +I_OMNISIGHT = "Omnisight Mapping Unit"; T_RAGEKIT = "Ragekit"; T_REFRESHER = "Refresher"; T_SANDWICH = "Grilled Cheese Sandwich"; diff --git a/modeldef.pickups b/modeldef.pickups index 1a87fcead..993fe132e 100644 --- a/modeldef.pickups +++ b/modeldef.pickups @@ -171,3 +171,41 @@ Model "Ragekit" FrameIndex XZW1 A 0 0 } + +Model "Omnisight" +{ + Path "models" + Model 0 "Omnisight_d.3d" + Skin 0 "Omnisight.png" + Scale 0.04 0.04 0.04 + RollOffset 30 + ZOffset 16 + ROTATING + + FrameIndex XZW1 A 0 0 +} + +Model "CompanionLamp" +{ + Path "models" + Model 0 "Lamp_d.3d" + Skin 0 "Lamp_off.png" + Scale 0.03 0.03 0.03 + ZOffset 8 + + FrameIndex XZW1 A 0 0 + Skin 0 "Lamp.png" + FrameIndex XZW1 B 0 0 +} + +Model "SWWMLamp" +{ + Path "models" + Model 0 "Lamp_d.3d" + Skin 0 "Lamp_off.png" + Scale 0.05 0.05 0.05 + ZOffset 16 + ROTATING + + FrameIndex XZW1 A 0 0 +} \ No newline at end of file diff --git a/sndinfo.txt b/sndinfo.txt index 41ba30244..8b8b40704 100644 --- a/sndinfo.txt +++ b/sndinfo.txt @@ -443,3 +443,6 @@ menu/demoscroll sounds/menu/menuscroll.ogg menu/democlose sounds/menu/menuclose.ogg menu/noinvuse sounds/menu/failinv.ogg + +$alias misc/invchange menu/demosel +UseArtifact DSEMPTY diff --git a/zscript/swwm_armor.zsc b/zscript/swwm_armor.zsc index 1cf24ce68..f36bc6e6d 100644 --- a/zscript/swwm_armor.zsc +++ b/zscript/swwm_armor.zsc @@ -4,11 +4,10 @@ Class ArmorNugget : SWWMArmor Default { Inventory.Icon "graphics/HUD/Icons/I_ArmorNugget.png"; - Inventory.Amount 1; + Inventory.Amount 5; Inventory.MaxAmount 200; Inventory.InterHubAmount 200; SWWMArmor.ArmorPriority 10; - SWWMArmor.DrainFactor .1; SWWMArmor.GiverArmor "ArmorNuggetItem"; } diff --git a/zscript/swwm_common.zsc b/zscript/swwm_common.zsc index 3ff4ed6cc..913a12edd 100644 --- a/zscript/swwm_common.zsc +++ b/zscript/swwm_common.zsc @@ -1515,15 +1515,22 @@ Class SWWMHandler : EventHandler else if ( e.Replacee == 'Medikit' ) e.Replacement = 'CubeHealthItem'; else if ( e.Replacee == 'ArtiHealth' ) { - if ( Random[Replacements](0,1) ) e.Replacement = 'CubeHealthItem'; + if ( gameinfo.gametype&GAME_Heretic && Random[Replacements](0,1) ) e.Replacement = 'CubeHealthItem'; else e.Replacement = 'TetraHealthItem'; } - else if ( (e.Replacee == 'Soulsphere') || (e.Replacee == 'ArtiSuperHealth') ) e.Replacement = 'RefresherItem'; + else if ( (e.Replacee == 'Soulsphere') || (e.Replacee == 'ArtiSuperHealth') ) + { + if ( gameinfo.gametype&GAME_Heretic ) e.Replacement = 'RefresherItem'; + else e.Replacement = 'CubeHealthItem'; + } + else if ( e.Replacee == 'ArtiHealingRadius' ) e.Replacement = 'RefresherItem'; else if ( (e.Replacee == 'Megasphere') || (e.Replacee == 'ArtiEgg') || (e.Replacee == 'PlatinumHelm') ) e.Replacement = 'GrilledCheeseSandwich'; else if ( (e.Replacee == 'Blursphere') || (e.Replacee == 'ArtiInvisibility') || (e.Replacee == 'AmuletOfWarding') ) e.Replacement = 'GhostArtifact'; else if ( (e.Replacee == 'Radsuit') || (e.Replacee == 'ArtiFly') || (e.Replacee == 'ArtiSpeedBoots') ) e.Replacement = 'GravitySuppressor'; else if ( (e.Replacee == 'InvulnerabilitySphere') || (e.Replacee == 'ArtiInvulnerability') || (e.Replacee == 'ArtiInvulnerability2') ) e.Replacement = 'FuckingInvinciball'; else if ( (e.Replacee == 'Berserk') || (e.Replacee == 'ArtiTomeOfPower') || (e.Replacee == 'ArtiDarkServant') || (e.Replacee == 'ArtiBoostArmor') ) e.Replacement = 'Ragekit'; + else if ( (e.Replacee == 'AllMap') || (e.Replacee == 'SuperMap') ) e.Replacement = 'Omnisight'; + else if ( (e.Replacee == 'Infrared') || (e.Replacee == 'ArtiTorch') || (e.Replacee == 'ArtiDarkServant') || (e.Replacee == 'ArtiBoostArmor') ) e.Replacement = 'SWWMLamp'; else if ( (e.Replacee == 'GreenArmor') || (e.Replacee == 'SilverShield') || (e.Replacee == 'MeshArmor') ) e.Replacement = 'BlastSuitItem'; else if ( (e.Replacee == 'BlueArmor') || (e.Replacee == 'FalconShield') || (e.Replacee == 'EnchantedShield') ) e.Replacement = 'WarArmorItem'; } diff --git a/zscript/swwm_health.zsc b/zscript/swwm_health.zsc index e3d140cb8..f0e93e4ef 100644 --- a/zscript/swwm_health.zsc +++ b/zscript/swwm_health.zsc @@ -3,7 +3,7 @@ Class HealthNugget : Health { Default { - Inventory.Amount 1; + Inventory.Amount 5; Inventory.MaxAmount 200; } } @@ -53,7 +53,7 @@ Class RefresherRegen : Powerup { Inventory.Icon "graphics/HUD/Icons/I_Refresher.png"; Powerup.Duration -60; - Powerup.Strength 10; + Powerup.Strength 5; } override void EndEffect() @@ -65,7 +65,7 @@ Class RefresherRegen : Powerup override void DoEffect() { Super.DoEffect(); - if ( Owner && (Owner.health > 0) && !(EffectTics%175) ) + if ( Owner && (Owner.health > 0) && !(EffectTics%70) ) { if ( Owner.GiveBody(int(Strength),500) ) { diff --git a/zscript/swwm_hud.zsc b/zscript/swwm_hud.zsc index 2bad73fa1..92643e6b0 100644 --- a/zscript/swwm_hud.zsc +++ b/zscript/swwm_hud.zsc @@ -27,8 +27,6 @@ Class SWWMStatusBar : BaseStatusBar double FracTic; int chatopen; - bool justselected; - DynamicValueInterpolator HealthInter, ScoreInter, FuelInter, DashInter; override void FlushNotify() @@ -103,18 +101,6 @@ Class SWWMStatusBar : BaseStatusBar override void Tick() { Super.Tick(); - if ( CPlayer.inventorytics >= (5*Thinker.TICRATE)-1 ) - { - if ( CPlayer.mo.InvSel ) - S_StartSound("menu/demoscroll",CHAN_BODY,CHANF_UI|CHANF_MAYBE_LOCAL); - } - if ( CPlayer.inventorytics > 0 ) justselected = false; - else - { - if ( !justselected && CPlayer.mo.InvSel ) - S_StartSound("menu/democlose",CHAN_BODY,CHANF_UI|CHANF_MAYBE_LOCAL); - justselected = true; - } if ( !chatduration ) chatduration = CVar.GetCVar('swwm_chatduration',players[consoleplayer]); if ( !msgduration ) msgduration = CVar.GetCVar('swwm_msgduration',players[consoleplayer]); if ( !pickduration ) pickduration = CVar.GetCVar('swwm_pickduration',players[consoleplayer]); diff --git a/zscript/swwm_inventory.zsc b/zscript/swwm_inventory.zsc index c838522c7..74c2298a7 100644 --- a/zscript/swwm_inventory.zsc +++ b/zscript/swwm_inventory.zsc @@ -16,12 +16,10 @@ Mixin Class SWWMAutoUseFix Class SWWMArmor : Armor abstract { int priority; - double reduction; String drainmsg; Class parent; Property ArmorPriority : priority; - Property DrainFactor : reduction; Property DrainMessage : drainmsg; Property GiverArmor : parent; @@ -32,7 +30,6 @@ Class SWWMArmor : Armor abstract +INVENTORY.UNDROPPABLE; +INVENTORY.KEEPDEPLETED; +INVENTORY.ALWAYSPICKUP; - SWWMArmor.DrainFactor 1.; } override void AttachToOwner( Actor other ) { @@ -65,10 +62,10 @@ Class SWWMArmor : Armor abstract SWWMHandler.DoFlash(Owner,Color(int(clamp(damage*.15,1,16)),255,224,192),3); Owner.A_StartSound("armor/hit",CHAN_BODY,CHANF_DEFAULT,clamp(damage*.03,0.,1.),2.5); saved = HandleDamage(damage,damageType,flags); - if ( amount <= int(ceil(saved*reduction)) ) saved = amount; + if ( amount <= saved ) saved = amount; newdamage -= saved; if ( newdamage < 0 ) Owner.GiveBody(abs(newdamage)); - amount -= int(ceil(saved*reduction)); + amount -= saved; damage = newdamage; if ( amount <= 0 ) { @@ -161,7 +158,9 @@ Class SWWMHealth : Inventory abstract if ( UseSound ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6); while ( (Amount > 0) && (newdamage > 0) ) { - newdamage = max(0,damage-GetDefaultByType(giveme).Amount); + newdamage = damage-GetDefaultByType(giveme).Amount; + if ( newdamage < 0 ) Owner.GiveBody(-newdamage,GetDefaultByType(giveme).MaxAmount); + newdamage = max(0,newdamage); AutoUseExtra(); Amount--; } diff --git a/zscript/swwm_menu.zsc b/zscript/swwm_menu.zsc index 53e451b46..ebafc6bac 100644 --- a/zscript/swwm_menu.zsc +++ b/zscript/swwm_menu.zsc @@ -15,7 +15,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu }; TextureID MainWindow, TabSeparator, WindowSeparator, WindowSeparatorH, - FancyBg; + FancyBg, EasterEgg; Font TewiFont; int curtab; // for scrolling @@ -59,11 +59,12 @@ Class SWWMKnowledgeBaseMenu : GenericMenu return; } TewiFont = Font.GetFont('TewiShaded'); - FancyBg = TexMan.CheckForTexture("graphics/tempbg.png",TexMan.Type_Any); - MainWindow = TexMan.CheckForTexture("graphics/KBase/MainWindow.png",TexMan.Type_Any); - TabSeparator = TexMan.CheckForTexture("graphics/KBase/TabSeparator.png",TexMan.Type_Any); - WindowSeparator = TexMan.CheckForTexture("graphics/KBase/WindowSeparator.png",TexMan.Type_Any); - WindowSeparatorH = TexMan.CheckForTexture("graphics/KBase/WindowSeparatorH.png",TexMan.Type_Any); + FancyBg = TexMan.CheckForTexture("graphics/tempbg.png",TexMan.Type_MiscPatch); + MainWindow = TexMan.CheckForTexture("graphics/KBase/MainWindow.png",TexMan.Type_MiscPatch); + TabSeparator = TexMan.CheckForTexture("graphics/KBase/TabSeparator.png",TexMan.Type_MiscPatch); + WindowSeparator = TexMan.CheckForTexture("graphics/KBase/WindowSeparator.png",TexMan.Type_MiscPatch); + WindowSeparatorH = TexMan.CheckForTexture("graphics/KBase/WindowSeparatorH.png",TexMan.Type_MiscPatch); + EasterEgg = TexMan.CheckForTexture("graphics/KBase/Drawing.png",TexMan.Type_MiscPatch); curtab = CVar.GetCVar('swwm_lasttab',players[consoleplayer]).GetInt(); if ( (curtab < 0) || (curtab > 7) ) curtab = 0; MenuSound("menu/demoopen"); @@ -637,9 +638,10 @@ Class SWWMKnowledgeBaseMenu : GenericMenu } else if ( curtab == TAB_SECRET ) { - // TODO easter egg "konami code" hidden tab - str = StringTable.Localize("$SWWM_COMINGSOON"); - Screen.DrawText(TewiFont,Font.CR_FIRE,(ss.x-TewiFont.StringWidth(str))/2.,(ss.y-TewiFont.GetHeight())/2.,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + // hidden secret tab + Screen.DrawTexture(EasterEgg,false,origin.x+20,origin.y+40,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + str = StringTable.Localize("$SWWM_TODEMO"); + Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+(640-TewiFont.StringWidth(str))/2.,origin.y+350,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); } } } diff --git a/zscript/swwm_powerup.zsc b/zscript/swwm_powerup.zsc index da7e1534a..ffa7a30af 100644 --- a/zscript/swwm_powerup.zsc +++ b/zscript/swwm_powerup.zsc @@ -401,7 +401,7 @@ Class InvinciballPower : Powerup Default { - Powerup.Duration -90; + Powerup.Duration -30; Inventory.Icon "graphics/HUD/Icons/I_Invinciball.png"; Powerup.Color "FF3000", 0.1; } @@ -666,3 +666,254 @@ Class Ragekit : Inventory Stop; } } + +Class Omnisight : Inventory +{ + override bool Use( bool pickup ) + { + level.allmap = true; + // not used up, must be kept for the targetting features to work + return false; + } + Default + { + Tag "$T_OMNISIGHT"; + Inventory.PickupSound "misc/p_pkup"; + Inventory.PickupMessage "$I_OMNISIGHT"; + Inventory.MaxAmount 1; + Inventory.InterHubAmount 0; + +INVENTORY.ALWAYSPICKUP; + +INVENTORY.AUTOACTIVATE; + +COUNTITEM; + +INVENTORY.BIGPOWERUP; + +INVENTORY.UNDROPPABLE; + +INVENTORY.UNTOSSABLE; + +FLOATBOB; + FloatBobStrength 0.25; + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +} + +Class LampMoth : Actor +{ +} + +Class LampMoth2 : LampMoth +{ +} + +Class LampMashiro : Actor abstract +{ +// +// ~nothing here yet, but she will make an appearance someday~ +// +// ⠀⠀⠀⠀⠤⠀⠄⠀⠀⠀⠳⠀⠂⠀⠀⠀⠀⠀⠀⠀⠀⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡣⣁⢌⡀⢄⠁⡁⠝⢿⣿⣿⣿⡻⣿⣿⣷⣦ +// ⠀⠀⠠⠀⠠⠀⠀⡀⠘⣠⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣊⢇⠠⢐⢄⡙⢿⣿⣿⣾⠫⣻⣿ +// ⠀⡄⠂⢴⠠⠌⠰⢇⠀⠀⠀⠀⠀⠀⠀⢀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⡀⠣⡣⡱⣌⠻⣿⣿⣿⣿⣿ +// ⠀⠁⠛⠂⠀⠉⡍⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⠘⣮⣾⡮⢦⡹⣿⣿⣿⣿ +// ⠄⠠⠉⢼⡇⠶⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⢿⣿⣿⡦⢣⡉⢝⢝⢽ +// ⠀⠀⠚⠃⠀⠀⡀⠀⠀⠀⠀⣠⣿⣿⣿⣿⣿⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⢸⣿⣿⣿⢀⢃⠀⡁⠑ +// ⠰⠀⠂⠁⠀⠀⡆⠀⠀⠀⣴⣿⣿⣿⣿⣿⠋⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡈⣿⣿⣿⣇⠆⢃⠈⡇ +// ⠀⠀⠀⢠⠄⣠⣇⠀⢀⣾⣿⣿⣿⣿⣿⡃⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⣿⣿⣿⣿⢔⢌⢆⢑ +// ⠀⠀⠀⢀⣶⣿⣷⢀⣾⣿⣿⣿⣿⡯⡊⠀⢸⡏⣟⣻⣽⣭⣽⣛⡛⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡯⡇⣿⣿⣿⣿⣗⢕⢕⠄ +// ⠀⢠⣶⣿⣿⢿⢃⣾⣿⣿⣿⣿⡫⡪⠀⠀⢸⡇⣿⣿⣿⣿⣿⣿⣿⣦⡘⠄⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡯⡇⣿⣿⣿⣿⣗⢔⢕⢅ +// ⠀⣺⣿⡉⠕⠁⣼⣿⣿⣿⡿⡫⡪⠂⠀⢔⢸⢀⠸⣿⣿⣿⣿⣿⣿⣿⣿⣶⣸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣪⠃⣿⣿⣿⣿⡟⢄⣡⣵ +// ⡛⠟⢋⠀⠀⢸⣿⣿⣿⣿⢠⢫⡊⠀⡔⢵⡈⢠⢣⡹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠡⠚⠓⠪⠉⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡫⡎⣸⣿⣿⣿⡟⢠⢃⣂⡿ +// ⡃⡊⢃⡔⡠⣿⣿⣿⣿⡇⣜⡘⢀⢜⡀⡅⡆⠘⢐⠁⠙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢾⠿⢿⣿⣿⣿⣿⣷⣾⣽⡻⢿⣿⣿⣿⣿⣿⣿⣿⡿⣛⣿⣿⣟⡜⠠⣻⣿⣿⡟⠴⠃⠉⠁⠀ +// ⡺⢠⡾⣸⡷⣿⣿⣿⣿⠂⣓⠂⡎⡺⠪⠒⠃⠀⠀⠀⠀⠀⠙⠿⣯⢻⣿⣿⣿⡟⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣵⣾⣿⡷⣲⣿⣿⣿⣿⣿⣿⣿⣷⣝⢿⣿⣿⣿⡿⡫⡺⣽⣿⣿⡗⠀⢸⣿⣿⠏⠀⠀⠀⠀⠀⠀ +// ⣴⣿⢣⣿⢳⣿⣿⣿⡿⡨⡊⠠⡪⡢⠂⠀⠀⠀⠀⠀⢀⣀⠀⠀⠘⢧⡻⣿⣿⡇⢿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣿⣿⣵⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢳⣝⠿⡫⡪⣪⡮⣾⣿⡟⢠⠅⣿⠟⠋⠀⠀⠀⠀⠀⠀⠀ +// ⣿⡏⣾⢣⡾⣿⣿⣿⡇⢪⠂⡨⡠⠀⠀⠀⠀⢀⡴⠋⠉⠈⠉⢦⡀⠠⢅⠹⣿⢰⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢟⣫⣾⡿⣫⣦⠸⢊⡄⣼⣿⡟⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +// ⡿⢸⣏⣾⣇⣿⣿⣿⡇⢵⢀⢓⠀⠀⠀⠀⠀⡮⠀⠀⠀⠀⠀⠈⣇⠀⠧⡗⡈⢿⣺⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⣯⣾⣿⡿⠛⠊⣬⠴⢪⠋⣼⣿⠟⢔⢝⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +// ⠃⡿⣸⣿⣿⢸⣿⣿⡇⡊⢰⢱⢂⡀⠀⠀⠀⢳⠀⠀⠀⠀⠀⢰⡇⠀⣸⢐⡈⢄⢿⠿⠿⢿⡿⠽⢫⣵⣿⡿⠿⠛⠛⠙⠉⠁⠀⠞⠙⠃⠍⡁⠖⡪⡫⡪⣻⢁⣾⡿⢃⢞⢕⡝⢰⣆⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +// ⢸⢣⣿⣿⡗⡈⢿⣿⡇⠀⢸⣿⣷⣵⣄⠀⠀⠈⠲⣠⣠⣠⡴⠋⠀⠀⢒⢔⢂⢀⢎⢇⣓⡕⠂⡚⡩⢡⢰⢂⣓⡣⣢⠒⠀⠀⠀⠀⠀⠀⠀⠈⠢⡈⡲⡑⢡⣾⠟⡠⡣⣂⢇⠃⣿⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀ +// ⡟⣾⣿⡿⣸⢸⡘⣿⣷⠀⢸⣿⣿⣿⣿⣿⣦⣄⣀⣀⣀⣀⣀⣠⣤⣜⢔⢕⢕⢔⢕⢕⣗⣇⣳⡪⣺⢐⢱⢑⢒⠖⠁⠀⠀⢀⠄⠔⠖⢦⣀⠀⠀⠰⠌⣴⠟⡡⡪⡪⡪⡜⡜⣸⣿⣿⣿⣿⡿⡢⠀⠀⠀⠀⠀⠀ +// ⢱⣿⣿⡣⢑⠔⡕⠘⣿⡀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣷⣷⣵⣕⣥⣣⣣⡣⡪⣐⢱⢠⡃⠀⠀⠀⣴⠁⠀⠀⠀⠀⠸⣆⠀⠀⠘⠀⡪⡪⡪⡪⡪⣸⢡⣿⣿⣿⣿⣿⡯⣺⡀⢤⡀⠀⠀⠀ +// ⣿⣿⣟⣝⣝⣺⠁⠀⠘⣧⢘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣷⣇⣣⠣⡘⡀⠀⠀⠀⡟⠀⠀⠀⠀⠀⠨⡗⠀⠀⠀⠈⢮⠺⡪⢊⢫⠃⣾⣿⣿⣿⣿⣿⡪⡏⣦⡀⢹⣷⣤⣀ +// ⣿⣿⢑⢇⢆⠆⠀⠀⣸⣎⠸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣮⣧⣀⠀⠀⢻⡀⠀⠀⠀⢀⡼⠃⠀⠀⠀⠀⠸⢘⢜⡐⡕⣸⣿⣿⣿⣿⣿⣃⣟⣱⣿⣿⣧⢻⣿⣿ +// ⣿⣟⢕⢕⢼⠀⠀⠀⣿⣿⣧⡹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⢡⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠀⠉⠓⠒⠓⠋⠀⠀⠀⠀⠀⠠⠀⠱⡱⡨⢣⣿⣿⣿⣿⣿⢗⡯⣱⣿⣿⣿⢇⣄⠃⠙ +// ⣿⡒⡢⡢⡃⠀⠀⠀⣿⣿⣿⣷⣝⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣤⣄⣀⣀⣀⣀⢀⣀⡀⡠⣀⡢⡪⠂⣽⣿⣿⣿⣿⣟⡕⣺⣿⣿⣿⣏⣿⣷⢔⡄ +// ⡿⡸⢰⢔⠂⠀⠀⠀⢿⣿⣿⣿⣿⣯⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣯⢮⡊⡠⣰⣿⣿⣿⣿⡿⣡⣿⣿⣿⣿⡟⣾⣿⣿⢵⠅ +// ⣟⢕⢝⣗⠀⠀⠀⡹⡘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣫⡾⢱⣿⣿⣿⣿⡟⣼⣿⣿⣿⣿⡿⣹⣿⣿⡟⡹⢰ +// ⢕⢕⢕⢕⠠⡢⣪⢂⢇⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣫⣾⣿⢣⣿⣿⣿⣿⣿⣻⣿⣿⣿⣿⡿⣱⣿⣿⣿⡫⡇⣼ +// ⢕⢕⢕⠅⣘⡪⡚⡔⠍⠂⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⣫⣾⣿⢟⢁⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢣⣿⣿⣿⣏⡼⢠⣿ +// ⢕⢕⢕⠀⣖⡪⡪⣂⠀⠐⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢟⣫⣵⡿⢟⣫⡴⢁⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⣸⣿⣿⣿⢒⠇⣼⣿ +// ⢕⢅⢕⠨⣃⢪⢨⢂⠀⠀⠀⠈⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⣳⢰⢬⡙⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣍⣛⠻⠿⠿⠿⢛⣯⡭⠶⣞⣛⣭⣵⣾⣿⠿⢁⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⢀⣿⣿⣿⡯⡝⢰⣿⣿ +// ⢝⢕⠇⢸⠱⡑⡁⠀⣀⠀⠀⠀⠈⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣷⣬⣃⣹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣾⣿⣿⣿⣿⣿⣿⣿⡿⡱⢃⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⠀⣼⣿⣿⣿⣸⢡⣿⣿⣿ +// ⢕⢕⠅⢜⠕⡠⣢⣾⣿⣷⣤⣄⢀⣬⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢛⠴⢡⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠁⠀⢠⣿⣿⣿⢝⢡⣿⣿⣿⣿ +// ⢕⢕⠅⣕⢥⡶⢀⠻⣿⣿⣿⣟⢿⣿⣿⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢫⡐⣕⢡⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠀⠀⢀⣿⣿⣿⠏⢁⣿⣿⣿⣿⣿ +// ⢕⢕⢠⢗⣛⣓⣁⢱⣾⣿⣿⣿⣷⣷⣽⣿⡻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢛⢔⠕⠓⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠀⠀⢀⣾⣿⡿⡣⢎⣿⣿⣿⣿⣿⣿ +// ⡕⡕⠠⡅⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡝⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠋⠐⣁⡴⡴⢣⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠃⠀⠀⢠⣾⠿⣫⠞⣱⣿⣿⣿⣿⣿⠟⡡ +// ⢅⢇⢘⢕⢍⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣎⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠿⠛⠛⠛⠉⠁⠀⢀⢜⢕⢅⠕⣡⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⠀⠐⡫⡠⠜⣡⣾⣿⣿⡿⠟⠫⠦⠪⠊ +// ⢕⢕⢸⣵⣷⣾⣿⣿⣿⣿⣟⢿⣿⣿⣿⣿⣿⣿⣿⣦⣤⡩⣉⣉⡩⣉⣉⠉⠉⠈⠀⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⠀⣤⢔⢗⢕⠕⣰⣿⣿⣿⣿⣿⣿⣿⣿⡿⢋⢔⢕⠕⣓⠏⣊⠰⠻⠟⠛⣉⠅⢄⠂⡪⡪⡪⠨ +// +} + +Class CompanionLamp : Actor +{ + Vector3 Trail; + Array moff; + + Default + { + +NOGRAVITY; + +NOCLIP; + +SHOOTABLE; + +NODAMAGE; + +NOBLOOD; + +DONTSPLASH; + +FLOATBOB; + +INTERPOLATEANGLES; + Radius 4; + Height 8; + FloatBobStrength 0.5; + } + action void A_Moth() + { + // random chance to spawn moths + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + if ( !target || !SWWMLamp(master) ) + { + Destroy(); + return; + } + Trail = pos; + } + override void Tick() + { + Super.Tick(); + if ( !target || !SWWMLamp(master) ) + { + Destroy(); + return; + } + // update trailing position + bool foundspot = false; + for ( int i=0; i<180; i++ ) + { + for ( int j=1; j>=-1; j-=2 ) + { + double ang = (target.angle-180)+i*j; + Vector3 testpos = target.Vec3Offset(cos(ang)*32,sin(ang)*32,target.height-8); + if ( !level.IsPointInLevel(testpos) ) continue; + Trail = testpos; + foundspot = true; + } + // check at most for a 45 degree offset + if ( foundspot && (i > 45) ) break; + } + angle += Clamp(deltaangle(angle,AngleTo(target)),-5.,5.); + vel *= .8; + Vector3 newpos = Trail; + Vector3 dir = level.Vec3Diff(pos,newpos); + if ( dir.length() > 0 ) + vel += dir.unit()*min(dir.length()*.05,20.); + Vector3 diff = pos-target.pos; + if ( (diff.x > -16) && (diff.x < 16) && (diff.y > -16) && (diff.y < 16) && (diff.z > -16) && (diff.z < target.height+8) ) + { + if ( diff.x < 0 ) vel.x -= .2; + else vel.x += .2; + if ( diff.y < 0 ) vel.y -= .2; + else vel.y += .2; + if ( diff.z < 0 ) vel.z -= .2; + else vel.z += .2; + } + } + States + { + Spawn: + XZW1 A 1 A_JumpIf(SWWMLamp(master)&&SWWMLamp(master).bActive,1); + Wait; + XZW1 B 0 A_JumpIf(!SWWMLamp(master)||!SWWMLamp(master).bActive,"Spawn"); + XZW1 B 1 A_Moth(); + Loop; + } +} + +Class SWWMLamp : Inventory +{ + bool bActive; + TextureID OnIcon; + Actor thelamp; + int charge; + + Property Charge : charge; + + override Inventory CreateCopy( Actor other ) + { + // additional lore + SWWMLoreLibrary.Add(other.player,"Lamp"); + return Super.CreateCopy(other); + } + override bool HandlePickup( Inventory item ) + { + // add charge + if ( item.GetClass() == GetClass() ) + Charge = min(default.Charge,Charge+SWWMLamp(item).Charge); + return Super.HandlePickup(item); + } + override bool Use( bool pickup ) + { + if ( pickup && !deathmatch ) return false; + bActive = !bActive; + if ( !OnIcon ) OnIcon = TexMan.CheckForTexture("graphics/HUD/Icons/I_Lamp.png",TexMan.Type_MiscPatch); + Icon = bActive?OnIcon:default.Icon; + // don't consume on use + Amount++; + return true; + } + override void DoEffect() + { + Super.DoEffect(); + if ( !thelamp ) + { + thelamp = Spawn("CompanionLamp",Owner.Vec3Offset(cos(Owner.angle-180)*32,sin(Owner.angle-180)*32,Owner.height-8)); + thelamp.target = Owner; + thelamp.master = self; + } + if ( bActive && !(level.maptime%35) ) Charge--; + if ( Charge <= 0 ) + { + Amount--; + if ( Amount <= 0 ) DepleteOrDestroy(); + } + } + override void DetachFromOwner() + { + Super.DetachFromOwner(); + if ( thelamp ) thelamp.Destroy(); + Icon = default.Icon; + bActive = false; + } + Default + { + Tag "$T_LAMP"; + Inventory.Icon "graphics/HUD/Icons/I_LampOff.png"; + Inventory.PickupSound "misc/p_pkup"; + Inventory.PickupMessage "$I_LAMP"; + Inventory.Amount 1; + Inventory.MaxAmount 1; + Inventory.InterHubAmount 1; + +INVENTORY.ALWAYSPICKUP; + +INVENTORY.AUTOACTIVATE; + +INVENTORY.INVBAR; + +COUNTITEM; + +INVENTORY.BIGPOWERUP; + +FLOATBOB; + FloatBobStrength 0.25; + SWWMLamp.Charge 100; + } + States + { + Spawn: + XZW1 A -1; + Stop; + } +}