diff --git a/decaldef.txt b/decaldef.txt index 018e4b39b..cb973274d 100644 --- a/decaldef.txt +++ b/decaldef.txt @@ -1,3 +1,79 @@ +decal WallCrack1 +{ + pic WallCrk1 + translucent 0.75 + shade "00 00 00" + x-scale 0.25 + y-scale 0.25 + randomflipx + randomflipy +} +decal WallCrack2 +{ + pic WallCrk2 + translucent 0.75 + shade "00 00 00" + x-scale 0.25 + y-scale 0.25 + randomflipx + randomflipy +} + +decalgroup WallCrack +{ + WallCrack1 1 + WallCrack2 1 +} + +decal ShockMark +{ + pic shockmrk + shade "00 00 00" + randomflipx + randomflipy + x-scale 0.6 + y-scale 0.6 +} +decal ShockMarkBig +{ + pic shockmrk + shade "00 00 00" + randomflipx + randomflipy + x-scale 1.5 + y-scale 1.5 +} + +decal BigBlast +{ + pic bigblast + shade "00 00 00" + randomflipx + randomflipy + x-scale 1.5 + y-scale 1.5 +} + +decal ImpactMark +{ + pic impcrack + shade "00 00 00" + randomflipx + randomflipy + x-scale 0.5 + y-scale 0.5 +} + +decal RipBlast +{ + pic ripblast + shade "00 00 00" + randomflipx + randomflipy + x-scale 0.45 + y-scale 0.45 +} + decal HugeScorch { pic SCORCH1 diff --git a/graphics/Decals/WallCrk1.png b/graphics/Decals/WallCrk1.png new file mode 100644 index 000000000..729b88ff0 Binary files /dev/null and b/graphics/Decals/WallCrk1.png differ diff --git a/graphics/Decals/WallCrk2.png b/graphics/Decals/WallCrk2.png new file mode 100644 index 000000000..ab82d7682 Binary files /dev/null and b/graphics/Decals/WallCrk2.png differ diff --git a/graphics/Decals/bigblast.png b/graphics/Decals/bigblast.png new file mode 100644 index 000000000..cfcadb4bd Binary files /dev/null and b/graphics/Decals/bigblast.png differ diff --git a/graphics/Decals/impcrack.png b/graphics/Decals/impcrack.png new file mode 100644 index 000000000..b1fda221c Binary files /dev/null and b/graphics/Decals/impcrack.png differ diff --git a/graphics/Decals/mcblast.png b/graphics/Decals/mcblast.png new file mode 100644 index 000000000..8dc06515c Binary files /dev/null and b/graphics/Decals/mcblast.png differ diff --git a/graphics/Decals/ripblast.png b/graphics/Decals/ripblast.png new file mode 100644 index 000000000..7aa1f4eb3 Binary files /dev/null and b/graphics/Decals/ripblast.png differ diff --git a/graphics/Decals/shockmrk.png b/graphics/Decals/shockmrk.png new file mode 100644 index 000000000..50719f1bd Binary files /dev/null and b/graphics/Decals/shockmrk.png differ diff --git a/graphics/HUD/EnemyBar.png b/graphics/HUD/EnemyBar.png new file mode 100644 index 000000000..da362e9de Binary files /dev/null and b/graphics/HUD/EnemyBar.png differ diff --git a/graphics/HUD/EnemyBox.png b/graphics/HUD/EnemyBox.png new file mode 100644 index 000000000..d43ebd9aa Binary files /dev/null and b/graphics/HUD/EnemyBox.png differ diff --git a/language.txt b/language.txt index 8f9737298..bb4e109cc 100644 --- a/language.txt +++ b/language.txt @@ -125,25 +125,25 @@ SWWM_TRADECONTROLS0 = "Enter: Select | "; SWWM_TRADECONTROLS1 = "Enter: Send | Backspace: Return | "; // mission entries SWWM_MISSION_DOOM = -"Welcome to your first mission, Demolitionist. Since I have very little confidence in those UAC idiots, I have decided to send you up ahead to assist on the fight against the demon invaders that came to Earth, thanks to their very wonderful and very reliable teleportation experiments backfiring, which is something absolutely no one saw coming, oh no, definitely not. It's not like that hasn't happened like... several times now? If Samuel Hayden didn't have me blocked I'd be sending him all sorts of trash right now. Oh I'd love to see his reaction to this...\n" +"Welcome to your first mission, Demolitionist. Since I have very little confidence in those UAC idiots, I have decided to send you up ahead to assist on the fight against the demon invaders that came to Earth, thanks to their very wonderful and very reliable teleportation experiments backfiring, which is something absolutely no one saw coming, oh no, definitely not. It's not like that hasn't happened like... several times now? The UAC's \"problem solver\" may not be able to do much, so yeah, better send you in to make a REAL difference here. If Samuel Hayden didn't have me blocked I'd be sending him all sorts of trash right now. Oh I'd love to see his reaction to this... \n" "\n" "Anyway, let's get to the point here. Your main objective is to eliminate all hostile forces in the region. There's going to be a hell a of a lot of fighting up ahead, but I know you can do it, that's why I built you. Do this job well and maybe you'll be rewarded when you come back home, who knows. No, Ibuki won't hug you again, I promise, your chassis will be safe. Your reward will actually be something else, but I won't tell you more details, I want it to be a surprise.\n" "\n" -"By the way, we haven't been able to provide you with all the available equipment, it would've been very expensive. Instead, be on the lookout for strategically placed supplies along the way. If that sounds too videogamey, you can eat my pants, I wasn't the one who made that decision. Whatever.\n" +"By the way, we haven't been able to provide you with all the available equipment, it would've been very expensive. Instead, be on the lookout for strategically placed supplies along the way (hopefully the demons haven't stolen all of it yet). If that sounds too videogamey, you can eat my pants, I wasn't the one who made that decision. Whatever.\n" "\n" "Saya out."; SWWM_MISSION_HERETIC = "Welcome to your second mission, Demolitionist. This one will be quite different, as you may see. While scouring the multiverse with our new portal technology we came across this cool little place called Parthoris. It's got elves 'n magic stuff, very fantasy-like setting, I dig it. Anyway I hope the ride wasn't too bumpy, crossing between universes and all.\n" "\n" -"Here's what's up: We have information that there is some sort of ongoing war between a bunch of elves and some crazy cult. Your objective is to neutralize this cult, preferably also taking out its leader, some sort of wizard dude. They call him D'Sparil and he's part of a triad of big baddies called the \"Serpent Riders\", because they ride dragons or something I guess? Guy's tall, wears red robes, and talks backwards, can't miss him. I'm sure that when you get back you'll want to transfer over to your maidbot body again, so I'll be working on a couple extensions there to make things more... \"fun\", if you catch my drift.\n" +"Here's what's up: We have information that there is some sort of ongoing conflict between a bunch of elves and some crazy cult that took over. Your objective is to neutralize this cult, preferably also taking out its leader, some sort of wizard dude. They call him D'Sparil and he's part of a triad of big baddies called the \"Serpent Riders\", because they ride those beasts I guess? Guy's tall, wears red robes, and talks backwards, can't miss him. I'm sure that when you get back you'll want to transfer over to your maidbot body again, so I'll be working on a couple extensions there to make things more... \"fun\", if you catch my drift. Oh, I'm in the mood for some eggplant right now, yes...\n" "\n" -"Once again, I'm sorry that we couldn't give you all your stuff. I'm taking the blame this time, I think I screwed something up with the deployment and it got scattered all over the place. It's possible that the locals may have also moved stuff around so... good luck finding it all.\n" +"So... uh... once again, I'm sorry that we couldn't give you all your stuff. I'm taking the blame this time, I think I screwed something up with the deployment and it got scattered all over the place. It's possible that the locals may have also moved stuff around so... good luck finding it all.\n" "\n" "Saya out."; SWWM_MISSION_HEXEN = -"Welcome to your third mission, Demolitionist. We're sending you into another world, but this is actually related to your previous mission. First of all, yes I fucked up again, I'm sorry, really. All your stuff got lost... again, my bad. On top of that there was some sort of mixup with the ammo supplies and instead we got these weird \"Fabricator\" things from Cyrus. They do provide ammo, at least.\n" +"Welcome to your third mission, Demolitionist. We're sending you into another world, but this is actually related to your previous mission. First of all, yes I fucked up again, I'm sorry, really. All your stuff got lost... again, my bad. On top of that there was some sort of mixup with the ammo supplies and instead we got these weird \"Fabricator\" things from that nerdo. They do provide ammo, at least.\n" "\n" -"So yeah, you're now in the land of Cronos, more of that cool dark fantasy stuff around. It seems that some old pal of that wizard dude from last time is being a stinky ass here, and we need that sorted out before we can start studying this place in more detail. So there you go, beat the crap outta him! As a reward, we can do some more of \"that\". Just you, me, maybe Ibuki too... hnnn... and then... *heavy breathing*\n" +"So yeah, you're now in the land of Cronos, more of that cool dark fantasy stuff around. It seems that some old pal of that wizard dude from last time is being a stinky ass here, and we need that sorted out before we can start studying this place in more detail. So there you go, beat the crap outta him! As a reward, we can do some more of \"that\". Just you, me, maybe Ibuki and Maidbot too... and I wonder... if Kirin-kun also... hnnn... and then... *heavy breathing*\n" "\n" "OK, wow... phew. Saya, focus. No hornyposting on mission statements. Anyway, more details about your target. Evil dragonish-looking dude, deep voice, and also a Serpent Rider, though I can't even begin to understand how he'd ride one. Maybe he's the one that receives the riding... u... ggdfgfhfhdfh... ok, pls no, BAD mental image, UGH. I think I'm going to need to spend a couple more hours drowning in Ibuki's tiddies to get this out of my head...\n" "\n" @@ -164,7 +164,7 @@ SWWM_LORETXT_BELT = "Designation: Adaptable Magnetic Utility Belt\n" "Manufacturer: Symnatek\n" "\n" -"Summary: A simple magnetic belt, adaptable to any waist size by detaching/attaching segments.\n" +"Summary: A simple magnetic belt, adaptable to any waist size by detaching/attaching segments. Its magnetic force is enough to keep any objects tightly attached no matter the force of impact the user faces.\n" "\n" "Addendum: This belt should allow you easy access to your Hammerspace containers and keychain, along with making it easier to reload certain weapons."; SWWM_LORETAG_BLASTSUIT = "Blast Suit"; @@ -200,7 +200,7 @@ SWWM_LORETXT_CRONOS = "Designation: Cronos\n" "Classification: Collection of worlds\n" "\n" -"Summary: A cluster of pocket worlds connected by strange portals. Historical research says that these were once all part of a much larger, more compact realm, but some great cataclysm happened and it all got shattered. Fortunately some \"sages\" devised a way to connect the many hubworlds together once again.\n" +"Summary: A cluster of pocket worlds connected by strange portals. Historical research says that these were once all part of a much larger, more compact realm, but some great cataclysm happened and it all got shattered. Fortunately some \"sages\" devised a way to connect the many hubworlds together once again. We've also been informed that there are three major organizations at work here, but they're not friendly at all, since they've allied with a Serpent Rider. You may have to confront their leaders before facing the one pulling the strings.\n" "\n" "Addendum: The natives are known to enjoy puzzles, sometimes to a sickening degree.\n" "\n" @@ -222,9 +222,9 @@ SWWM_LORETXT_DEEPIMPACT = "\n" "Summary: The Deep Impact is a compressed air gun, allegedly meant to be a child's toy. Its maximum pressure allows for deadly blasts of air that could cause severe internal hemorrhaging and crushing of vital organs.\n" "\n" -"Primary Fire: Quick puffs of compressed air. This pocket of quickly expanding air can push away obstacles in front of the user, or, if aimed at a solid surface, allow the user to boost their jump height. It also deals considerable damage.\n" +"Primary Fire: Quick puffs of compressed air. Pushes away enemies and projectiles, while also dealing some damage.\n" "\n" -"Secondary Fire: Compresses all the air into a single shot with very considerable force. This \"air bullet\" is capable of penetrating multiple targets, digging a hole staight through their bodies. Requires a full air tank.\n" +"Secondary Fire: Compresses all the air into a single highly destructive shot. This \"air bullet\" is capable of penetrating multiple targets, digging a hole staight through their bodies. Requires a full air tank.\n" "\n" "Reloading: The lever on the side must be pulled several times in order to refill the internal air tank. Once at full capacity, a smart mechanism will lock the lever to avoid accidental overfilling.\n" "\n" @@ -236,7 +236,9 @@ SWWM_LORETXT_DEMOLITIONIST = "Nationality: Japanese\n" "Date Of Birth: 2148-01-20\n" "\n" -"Summary: The Demolitionist is a refinement of the former Red Oni supersoldier program, an AI-controlled combat unit capable of efficiently utilizing any weaponry at its disposition. The first unit (that's you) was deployed in June of 2148 on Union States territory in order to combat the demonic invasion brought upon by the UAC's interventions on Hell.\n" +"Summary: The Demolitionist is a refinement of the former Red Oni supersoldier program, an AI-controlled combat unit capable of efficiently utilizing any weaponry at its disposition. Its AI core is built on the same technology as Akari Labs' maidbots, the Nekuronbot AI framework, provided as a gift of friendship between Saya and Zanaveth II, the current head of Nekuratek. Thanks to this technology, the Demolitionist is barely indistinguishable from a real person in its behaviour and emotional response, which makes interaction with civilians more comforting.\n" +"\n" +"Addendum: The first unit (that's you) was deployed in June of 2148 on Union States territory in order to combat the demonic invasion brought upon by the UAC's interventions on Hell.\n" "\n" "Saya's Note: Stop reading about yourself and get back to work."; SWWM_LORETAG_DISPLAY = "CuteEmotion Display"; @@ -258,7 +260,7 @@ SWWM_LORETXT_DOOMGUY = "\n" "Addendum: You have been ordered to not interact with this man, if you encounter him, please stand back and let him work.\n" "\n" -"Saya's Note: Sorry about the lack of info here, those UAC goons really keep it well-guarded."; +"Saya's Note: Sorry about the lack of info here, those UAC goons really keep it well-guarded. Hell, I've even heard there's ANOTHER guy like this one, but he's like, ten times more hardcore? Some kinda ancient alien warrior or something, according to the leaks I saw (very little info here too, yeah)."; SWWM_LORETAG_EXPLODIUMGUN = "Explodium Gun"; SWWM_LORETXT_EXPLODIUMGUN = "Designation: Explodium Gun\n" @@ -281,7 +283,7 @@ SWWM_LORETXT_FORX = "\n" "Summary: Founded by Harold Forx in 2029, this company is credited with singlehandedly rescuing the UK from its long fall into economic ruin after Brexit. Forx Aeronautics is the #1 supplier of propulsion engines for all types of aircraft, be it commercial jet planes, or even spaceships. The only exception being the UAC, who prefer their own in-house technology. Forx has also adapted his patented jet propulsion systems for other uses, such as in the \"JetBurst Impulsor\", one of the first tuly portable jetpacks, or the \"Rapid Jet Compensator\", a recoil dampening system for high caliber firearms that permits their use outside of heavy, unwieldy power armor.\n" "\n" -"Saya's Note: Hey did you hear the story about that one time he tried to mow down an orphanage and the guy who ran it gave him a smack so fucking hard he did a complete 180 and left it alone? I've seen the archived footage like a dozen times and I always lose my shit with that asshole's face when he comes out."; +"Saya's Note: Hey did you hear the story about that one time he tried to mow down an orphanage and the guy who ran it smacked him so fucking hard he did a complete 180 and ran away crying? I've seen the archived footage like a dozen times and I always lose my shit with that asshole's face when he comes out."; SWWM_LORETAG_HAMMERSPACE = "Hammerspace"; SWWM_LORETXT_HAMMERSPACE = "Designation: Pocket Hammerspace Container\n" @@ -307,7 +309,7 @@ SWWM_LORETXT_IBUKI = "Nationality: Japanese\n" "Date of Birth: 2074-09-09\n" "\n" -"Summary: Ibuki is the result of the Red Oni supersoldier program at Akari Labs, commissioned by the US government. Her purpose was to turn around the losing World War 3 that began due to a very badly received tweet from the President of the United States, an event reminiscent of the incident with Iran that the late Donald Trump had started. In a mere three years she was ready, thanks to an accelerated growth process. She's been enhanced to be effectively bulletproof, always standing strong under any circumstance, capable of handling extreme heat and cold, and trained for handling all kinds of weaponry. Much is also said about her personality, or her questionable physical traits such as the ludicrously impractical bust size, all of this which must clearly have been intentionally done by her creator and self-appointed \"mother\", Saya Miyamoto. After the end of the war she's gone through several jobs, crowning in her current one as a world-famous nude model and vlogger.\n" +"Summary: Ibuki is the result of the Red Oni supersoldier program at Akari Labs, commissioned by the US government. Her purpose was to turn the tides on the losing World War 3 that began due to a very badly received tweet from the President of the United States, an event reminiscent of the incident with Iran that the late Donald Trump had started. In a mere three years she was ready, thanks to an accelerated growth process. She's been enhanced to be effectively bulletproof, always standing strong under any circumstance, capable of handling extreme heat and cold, and trained for handling all kinds of weaponry. Much is also said about her personality, or her questionable physical traits such as the ludicrously impractical bust size, all of this which must clearly have been intentionally done by her creator and self-appointed \"mother\", Saya Miyamoto. After the end of the war she's gone through several jobs, crowning in her current one as a world-famous nude model and vlogger.\n" "\n" "Addendum: In a way, the Demolitionist project could be said to be a more economic alternative to the Red Oni program. Robots are easy to manufacture en-masse, after all.\n" "\n" @@ -318,7 +320,7 @@ SWWM_LORETXT_LOCKE = "Nationality: Qurensniv\n" "Date of Birth: 1980-07-30\n" "\n" -"Summary: A man who changed entirely after the loss of his older brother in 1987 during a very famous oil rig explosion near the town of Kereshnovka. Nowadays he's pretty much just a mad scientist shut-in who sometimes creates \"toys\" that are hilariously deadly. In 2049 he attempted to \"take over the world\", an event that backfired so hilariously he completely disappeared from the public eye afterwards.\n" +"Summary: A man who changed entirely after the loss of his older brother in 1987, in an oil rig explosion near the town of Kereshnovka. Nowadays he's pretty much just a mad scientist shut-in who sometimes creates \"toys\" that are hilariously deadly. In 2049 he attempted to \"take over the world\", an event that backfired so hilariously he completely disappeared from the public eye afterwards.\n" "\n" "Saya's Note: Guy's a total nut, but these \"toys\" are very reliable weapons for you. Just be careful when using them, shit could backfire catastrophically at any moment, who knows..."; SWWM_LORETAG_MUNCH = "Munch Innovations"; @@ -337,7 +339,7 @@ SWWM_LORETXT_PARTHORIS = "Designation: Parthoris\n" "Classification: Alternate world\n" "\n" -"Summary: One of the other worlds we've discovered during the initial interportal experiments. This is a land stuck in some sort of medieval-ish, fantasy type setting. Although some of our explorers have reported sights of unusually complex technology in a certain location we haven't been able to approach.\n" +"Summary: One of the other worlds we've discovered during the initial interportal experiments. This land is divided in seven major nations we don't yet know the name of, and appears to be stuck in some sort of a medieval-ish fantasy type setting, although some of our explorers have reported sights of unusually complex technology in a certain location we haven't been able to approach. As of now there is some hostility in the area what with a bunch of evildoers having taken over by force.\n" "\n" "Addendum: You are our ambassador here, so I hope you make a good impression."; SWWM_LORETAG_PROPULSOR = "JetBurst Impulsor"; @@ -365,9 +367,9 @@ SWWM_LORETXT_SERPENTRIDERS = "Classification: Evil, definitely\n" "Origin: Undetermined\n" "\n" -"Summary: A triad of evildoers known all over these lands. All those who've dared to fight against them haven't been very lucky. D'Sparil claimed the land of Parthoris, his many armies of undead having ravaged the land long before our arrival here. Then in the land of Cronos there's Korax, a black beast that taunts all those who challenge him. We haven't received much information on the third member, but this stuff will come eventually as we keep exploring these new worlds.\n" +"Summary: A triad of evildoers known all over these lands. All those who've dared to fight against them haven't been very lucky. D'Sparil claimed the land of Parthoris, his many armies of undead having taken over the land before our arrival. Then in the land of Cronos there's Korax, a black beast that taunts all those who challenge him. We haven't received much information on the third member, but this stuff will come eventually as we keep exploring these new worlds.\n" "\n" -"Saya's Note: I'm sure you can wipe out all these idiots without any issue, they have no idea of how strong you are (or of what you are, now that I think of it, this world hardly seems to know much about our technology)."; +"Saya's Note: I'm sure you can wipe out all these idiots without any issue, they have no idea of how strong you are (or of what you are, now that I think of it, these worlds hardly seem to know much about our technology)."; SWWM_LORETAG_UAC = "UAC"; SWWM_LORETXT_UAC = "Name: Union Aerospace Corporation\n" @@ -392,7 +394,7 @@ SWWM_LORETXT_SIDHE = "Designation: Sidhe\n" "Location: Parthoris\n" "\n" -"Summary: An elf species native to this world, they have a natural bond with magic and are known to be great fighters. As the story goes they however face persecution by other groups, led mainly by the cult of D'Sparil, one of the Serpent Riders who has made the land his own.\n" +"Summary: An elf species native to this world, they have a natural bond with magic and are known to be great fighters. As the story goes they however face persecution by other groups due to the opposition to the current ruler. Most of them have been killed in the war, only few remain. It is your mission to help them.\n" "\n" "Saya's Note: How do you exactly pronounce \"Sidhe\" anyway?"; SWWM_LORETAG_HAMMERSPACEEMBIGGENER = "Hs. Embiggener"; @@ -906,6 +908,13 @@ D_INVINCIBALL = "You are no longer invincible."; D_RAGEKIT = "The Ragekit has ragequit."; D_REFRESHER = "The Refresher boost has ended."; D_WARARMOR = "The War Armor is no more."; +// targetter +SWWM_USABLE = "Usable"; +SWWM_SHOOTABLE = "Shootable"; +SWWM_BOTHUSE = "Usable / Shootable"; +SWWM_EXIT = "Exit: "; +SWWM_NEXIT = "Exit"; +SWWM_SEXIT = "Secret Exit"; // score messages SWWM_FINDSECRET = "\cf%s\cf found a secret. +%d\c-"; SWWM_FINDKEY = "\cf%s\cf got the %s. +%d\c-"; diff --git a/modeldef.deepimpact b/modeldef.deepimpact index 3715f85c8..1449d87e0 100644 --- a/modeldef.deepimpact +++ b/modeldef.deepimpact @@ -11,3 +11,101 @@ Model "DeepImpact" FrameIndex XZW1 A 0 0 } + +Model "DeepImpact" +{ + Path "models" + + Model 1 "DeepImpact1st_d.3d" + SurfaceSkin 1 0 "DemoTags.png" + SurfaceSkin 1 1 "DemoArms.png" + SurfaceSkin 1 2 "DemoSoft.png" + SurfaceSkin 1 3 "DeepImpact.png" + + Scale -0.005 0.0025 0.005 + AngleOffset -90 + Offset 0 0 -2 + + // Still / Down / Select + FrameIndex XZW2 A 1 0 // still / down + FrameIndex XZW2 B 1 1 + FrameIndex XZW2 C 1 2 + FrameIndex XZW2 D 1 3 + FrameIndex XZW2 E 1 4 + FrameIndex XZW2 F 1 5 + FrameIndex XZW2 G 1 6 + FrameIndex XZW2 H 1 7 + FrameIndex XZW2 I 1 8 // enddown / startselect + FrameIndex XZW2 J 1 9 + FrameIndex XZW2 K 1 10 + FrameIndex XZW2 L 1 11 + FrameIndex XZW2 M 1 12 + FrameIndex XZW2 N 1 13 + FrameIndex XZW2 O 1 14 + FrameIndex XZW2 P 1 15 + // Fire + FrameIndex XZW2 Q 1 17 + FrameIndex XZW2 R 1 18 + FrameIndex XZW2 S 1 19 + FrameIndex XZW2 T 1 20 + FrameIndex XZW2 U 1 21 + // AltFire + FrameIndex XZW2 V 1 23 + FrameIndex XZW2 W 1 24 + FrameIndex XZW2 X 1 25 + FrameIndex XZW2 Y 1 26 + FrameIndex XZW2 Z 1 27 + FrameIndex XZW3 A 1 28 + FrameIndex XZW3 B 1 29 + FrameIndex XZW3 C 1 30 + FrameIndex XZW3 D 1 31 + FrameIndex XZW3 E 1 32 + FrameIndex XZW3 F 1 33 + FrameIndex XZW3 G 1 34 + // ReloadBeg + FrameIndex XZW3 H 1 36 + FrameIndex XZW3 I 1 37 + FrameIndex XZW3 J 1 38 + // Reload + FrameIndex XZW3 K 1 39 + FrameIndex XZW3 L 1 40 + FrameIndex XZW3 M 1 41 + FrameIndex XZW3 N 1 42 + FrameIndex XZW3 O 1 43 + FrameIndex XZW3 P 1 44 + FrameIndex XZW3 Q 1 45 + // ReloadEnd + FrameIndex XZW3 R 1 47 + FrameIndex XZW3 S 1 48 + FrameIndex XZW3 T 1 49 + FrameIndex XZW3 U 1 50 + FrameIndex XZW3 V 1 51 + // CheckGun + FrameIndex XZW3 W 1 53 + FrameIndex XZW3 X 1 54 + FrameIndex XZW3 Y 1 55 + FrameIndex XZW3 Z 1 56 + FrameIndex XZW4 A 1 57 + FrameIndex XZW4 B 1 58 + FrameIndex XZW4 C 1 59 + FrameIndex XZW4 D 1 60 + FrameIndex XZW4 E 1 61 + FrameIndex XZW4 F 1 62 + FrameIndex XZW4 G 1 63 + // Melee + FrameIndex XZW4 H 1 65 + FrameIndex XZW4 I 1 66 + FrameIndex XZW4 J 1 67 + FrameIndex XZW4 K 1 68 + FrameIndex XZW4 L 1 69 + FrameIndex XZW4 M 1 70 + FrameIndex XZW4 N 1 71 + FrameIndex XZW4 O 1 72 + FrameIndex XZW4 P 1 73 + FrameIndex XZW4 Q 1 74 + FrameIndex XZW4 R 1 75 + FrameIndex XZW4 S 1 76 + FrameIndex XZW4 T 1 77 + FrameIndex XZW4 U 1 78 + FrameIndex XZW4 V 1 79 +} diff --git a/models/DeepImpact1st.blend b/models/DeepImpact1st.blend new file mode 100644 index 000000000..2383c73d8 Binary files /dev/null and b/models/DeepImpact1st.blend differ diff --git a/models/DeepImpact1st_a.3d b/models/DeepImpact1st_a.3d new file mode 100644 index 000000000..1cf5d38b9 Binary files /dev/null and b/models/DeepImpact1st_a.3d differ diff --git a/models/DeepImpact1st_d.3d b/models/DeepImpact1st_d.3d new file mode 100644 index 000000000..599a066d2 Binary files /dev/null and b/models/DeepImpact1st_d.3d differ diff --git a/models/ModelNotes.txt b/models/ModelNotes.txt index 60b5414d8..93b3cf018 100644 --- a/models/ModelNotes.txt +++ b/models/ModelNotes.txt @@ -13,12 +13,12 @@ X candygun tertiary Deep Impact: -- down/select -- fire anim -- altfire anim -- pump -- "check out gun" idle anim -- melee bash +* down/select +* fire anim +* altfire anim +* pump +* "check out gun" idle anim +* melee bash Pusher: diff --git a/palettes/ImpactWav2.pal b/palettes/ImpactWav2.pal new file mode 100644 index 000000000..b17845ac1 Binary files /dev/null and b/palettes/ImpactWav2.pal differ diff --git a/sndinfo.txt b/sndinfo.txt index ddefbf7fd..23afe3bea 100644 --- a/sndinfo.txt +++ b/sndinfo.txt @@ -296,6 +296,9 @@ demolitionist/swing1 sounds/demolitionist/demoswing1.ogg demolitionist/swing2 sounds/demolitionist/demoswing2.ogg demolitionist/swing3 sounds/demolitionist/demoswing3.ogg $random demolitionist/swing { demolitionist/swing1 demolitionist/swing2 demolitionist/swing3 } +demolitionist/wswing1 sounds/demolitionist/demowswing1.ogg +demolitionist/wswing2 sounds/demolitionist/demowswing2.ogg +$random demolitionist/wswing { demolitionist/wswing1 demolitionist/wswing2 } demolitionist/punch1 sounds/demolitionist/demopunch1.ogg demolitionist/punch2 sounds/demolitionist/demopunch2.ogg demolitionist/punch3 sounds/demolitionist/demopunch3.ogg @@ -331,6 +334,22 @@ $playersound demolitionist neutral *gasp DSEMPTY $playersound demolitionist neutral *taunt DSEMPTY $playersound demolitionist neutral *evillaugh DSEMPTY +deepimpact/fire sounds/deepimpact/impfire.ogg +deepimpact/charge sounds/deepimpact/impaltcharge.ogg +deepimpact/altfire sounds/deepimpact/impaltfire.ogg +deepimpact/dryfire sounds/deepimpact/impdryfire.ogg +deepimpact/select sounds/deepimpact/impsel.ogg +deepimpact/checkout sounds/deepimpact/impidle.ogg +deepimpact/deselect sounds/deepimpact/impdown.ogg +deepimpact/bullet sounds/deepimpact/impbulletfly.ogg +deepimpact/bullethit1 sounds/deepimpact/impexplo1.ogg +deepimpact/bullethit2 sounds/deepimpact/impexplo2.ogg +deepimpact/reloadbeg sounds/deepimpact/impreloadstart.ogg +deepimpact/reloadend sounds/deepimpact/impreloadend.ogg +deepimpact/reload sounds/deepimpact/impcrank.ogg +deepimpact/noreload sounds/deepimpact/impcrankhalt.ogg +$random deepimpact/bullethit { deepimpact/bullethit1 deepimpact/bullethit2 } + explodium/casing1 sounds/explodiumgun/expl_case1.ogg explodium/casing2 sounds/explodiumgun/expl_case2.ogg explodium/casing3 sounds/explodiumgun/expl_case3.ogg diff --git a/sounds/deepimpact/impaltcharge.ogg b/sounds/deepimpact/impaltcharge.ogg new file mode 100644 index 000000000..b327a8b3d Binary files /dev/null and b/sounds/deepimpact/impaltcharge.ogg differ diff --git a/sounds/deepimpact/impaltfire.ogg b/sounds/deepimpact/impaltfire.ogg new file mode 100644 index 000000000..da6c36a9d Binary files /dev/null and b/sounds/deepimpact/impaltfire.ogg differ diff --git a/sounds/deepimpact/impbulletfly.ogg b/sounds/deepimpact/impbulletfly.ogg new file mode 100644 index 000000000..66509ed4c Binary files /dev/null and b/sounds/deepimpact/impbulletfly.ogg differ diff --git a/sounds/deepimpact/impcrank.ogg b/sounds/deepimpact/impcrank.ogg new file mode 100644 index 000000000..b50a205e8 Binary files /dev/null and b/sounds/deepimpact/impcrank.ogg differ diff --git a/sounds/deepimpact/impcrankhalt.ogg b/sounds/deepimpact/impcrankhalt.ogg new file mode 100644 index 000000000..afee0a27a Binary files /dev/null and b/sounds/deepimpact/impcrankhalt.ogg differ diff --git a/sounds/deepimpact/impdown.ogg b/sounds/deepimpact/impdown.ogg new file mode 100644 index 000000000..c940de8cd Binary files /dev/null and b/sounds/deepimpact/impdown.ogg differ diff --git a/sounds/deepimpact/impdryfire.ogg b/sounds/deepimpact/impdryfire.ogg new file mode 100644 index 000000000..01a14eb95 Binary files /dev/null and b/sounds/deepimpact/impdryfire.ogg differ diff --git a/sounds/deepimpact/impexplo1.ogg b/sounds/deepimpact/impexplo1.ogg new file mode 100644 index 000000000..cf0ebfb46 Binary files /dev/null and b/sounds/deepimpact/impexplo1.ogg differ diff --git a/sounds/deepimpact/impexplo2.ogg b/sounds/deepimpact/impexplo2.ogg new file mode 100644 index 000000000..8d35882a1 Binary files /dev/null and b/sounds/deepimpact/impexplo2.ogg differ diff --git a/sounds/deepimpact/impfire.ogg b/sounds/deepimpact/impfire.ogg new file mode 100644 index 000000000..0c1b407be Binary files /dev/null and b/sounds/deepimpact/impfire.ogg differ diff --git a/sounds/deepimpact/impidle.ogg b/sounds/deepimpact/impidle.ogg new file mode 100644 index 000000000..ed314f1eb Binary files /dev/null and b/sounds/deepimpact/impidle.ogg differ diff --git a/sounds/deepimpact/impreloadend.ogg b/sounds/deepimpact/impreloadend.ogg new file mode 100644 index 000000000..76f0e3864 Binary files /dev/null and b/sounds/deepimpact/impreloadend.ogg differ diff --git a/sounds/deepimpact/impreloadstart.ogg b/sounds/deepimpact/impreloadstart.ogg new file mode 100644 index 000000000..ea22d5a32 Binary files /dev/null and b/sounds/deepimpact/impreloadstart.ogg differ diff --git a/sounds/deepimpact/impsel.ogg b/sounds/deepimpact/impsel.ogg new file mode 100644 index 000000000..452ccbb01 Binary files /dev/null and b/sounds/deepimpact/impsel.ogg differ diff --git a/sounds/demolitionist/demowswing1.ogg b/sounds/demolitionist/demowswing1.ogg new file mode 100644 index 000000000..455fd99ea Binary files /dev/null and b/sounds/demolitionist/demowswing1.ogg differ diff --git a/sounds/demolitionist/demowswing2.ogg b/sounds/demolitionist/demowswing2.ogg new file mode 100644 index 000000000..3d6ca69b1 Binary files /dev/null and b/sounds/demolitionist/demowswing2.ogg differ diff --git a/zscript/swwm_common.zsc b/zscript/swwm_common.zsc index e19fb4458..f169e005f 100644 --- a/zscript/swwm_common.zsc +++ b/zscript/swwm_common.zsc @@ -250,6 +250,124 @@ Class SWWMLoreLibrary : Thinker } } +// floating scores +Class SWWMScoreObj : Thinker +{ + int tcolor; + int score; + Vector3 pos; + int lifespan, initialspan; + int starttic, seed, seed2; + + static SWWMScoreObj Spawn( int score, Vector3 pos, int tcolor = Font.CR_GOLD ) + { + let o = new("SWWMScoreObj"); + o.ChangeStatNum(STAT_USER); + o.score = score; + o.pos = pos; + o.lifespan = o.initialspan = 60; + o.tcolor = tcolor; + o.starttic = gametic; + o.seed = Random[ScoreBits](); + o.seed2 = Random[ScoreBits](); + return o; + } + + override void Tick() + { + lifespan--; + if ( lifespan <= 0 ) Destroy(); + } +} + +enum EInterestType +{ + INT_Key, + INT_Exit, + INT_Usable +}; + +Class SWWMInterest : Thinker +{ + int type; + Key trackedkey; + Line trackedline; + Vector3 pos; + + static SWWMInterest Spawn( Vector3 pos = (0,0,0), Key thekey = null, Line theline = null ) + { + if ( (!thekey && !theline) || (thekey && theline) ) return null; + let i = new("SWWMInterest"); + i.ChangeStatNum(STAT_USER); + i.trackedkey = thekey; + i.trackedline = theline; + if ( thekey ) i.type = INT_Key; + else if ( (theline.special == Exit_Normal) || (theline.special == Exit_Secret) || (theline.special == Teleport_EndGame) || (theline.special == Teleport_NewMap) ) + i.type = INT_Exit; + else i.type = INT_Usable; + i.pos = thekey?thekey.Vec3Offset(0,0,thekey.height/2):pos; + return i; + } + + override void Tick() + { + // update + if ( (type == INT_Key) && (!trackedkey || trackedkey.Owner) ) Destroy(); + else if ( trackedkey ) pos = trackedkey.Vec3Offset(0,0,trackedkey.height/2); + else if ( (type == INT_Usable) && !trackedline.special ) Destroy(); + } +} + +// enemy combat tracker +Class SWWMCombatTracker : Thinker +{ + Actor mytarget; + String mytag; + int updated, lasthealth, maxhealth; + transient DynamicValueInterpolator intp; + Vector3 pos, prevpos; + + static SWWMCombatTracker Spawn( Actor target ) + { + let t = new("SWWMCombatTracker"); + t.ChangeStatNum(STAT_USER); + t.mytarget = target; + t.mytag = target.player?target.player.GetUserName():target.GetTag(); + t.lasthealth = t.maxhealth = target.health; + t.updated = int.min; + t.pos = level.Vec3Offset(target.pos,(0,0,target.default.height)); + t.prevpos = level.Vec3Offset(target.prev,(0,0,target.default.height)); + t.intp = DynamicValueInterpolator.Create(t.lasthealth,.5,1,100); + return t; + } + + override void Tick() + { + // update + if ( mytarget ) + { + pos = level.Vec3Offset(mytarget.pos,(0,0,mytarget.default.height)); + prevpos = level.Vec3Offset(mytarget.prev,(0,0,mytarget.default.height)); + } + if ( !mytarget || (mytarget.Health <= 0) ) + { + // we're done + if ( updated > gametic ) updated = gametic; + lasthealth = 0; + intp.Update(lasthealth); + if ( gametic > updated+35 ) Destroy(); + return; + } + mytag = mytarget.player?mytarget.player.GetUserName():mytarget.GetTag(); + int newhealth = mytarget.Health; + if ( newhealth != lasthealth ) updated = gametic+35; + if ( mytarget.target && (mytarget.target.Health > 0) && (mytarget.target.player == players[consoleplayer]) && mytarget.CheckSight(mytarget.target) ) updated = gametic+70; + if ( mytarget.IsFriend(players[consoleplayer].mo) ) updated = gametic; + lasthealth = newhealth; + intp.Update(lasthealth); + } +} + // One-liners Class SWWMOneLiner : HUDMessageBase { @@ -678,13 +796,51 @@ Class SWWMItemFog : Actor } } +Class TeleLight : PaletteLight +{ + Default + { + Tag "ImpactWav"; + ReactionTime 10; + Args 0,0,0,150; + } +} + Class SWWMTeleportFog : Actor { + + Default + { + +NOGRAVITY; + +NOBLOCKMAP; + +DONTSPLASH; + } + override void PostBeginPlay() + { + Super.PostBeginPlay(); + SetOrigin(Vec3Offset(0,0,28),false); + A_StartSound("misc/teleport",CHAN_VOICE); + Spawn("TeleLight",pos); + } States { Spawn: - TNT1 A 1; - Stop; + TNT1 A 1 + { + int numpt = int(Random[ExploS](16,32)*alpha); + for ( int i=0; i skipme; + skipme.Clear(); + // find exit lines, and use lines that aren't exits + for ( int i=0; i skipme.Size() ) continue; + Vector3 al, ah, bl, bh; + // TODO properly center on the usable part + al = (l.v1.p.x,l.v1.p.y,l.frontsector.floorplane.ZatPoint(l.v1.p)); + ah = (l.v1.p.x,l.v1.p.y,l.frontsector.ceilingplane.ZatPoint(l.v1.p)); + bl = (l.v2.p.x,l.v2.p.y,l.frontsector.floorplane.ZatPoint(l.v2.p)); + bh = (l.v2.p.x,l.v2.p.y,l.frontsector.ceilingplane.ZatPoint(l.v2.p)); + Vector3 lpos = (al+ah+bl+bh)*.25; + if ( (l.special == Exit_Normal) || (l.special == Exit_Secret) || (l.special == Teleport_EndGame) || (l.special == Teleport_NewMap) ) + { + // look for connected lines + int xcnt = 1; + if ( l.frontsector ) + { + for ( int j=0; j 0 ) + SWWMScoreObj.Spawn(-e.Damage,e.Thing.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+e.Thing.Height/2),Font.CR_RED); if ( e.Thing.player ) { tookdamage[e.Thing.PlayerNumber()] = true; @@ -1182,7 +1398,8 @@ Class SWWMHandler : EventHandler } if ( (e.DamageSource == players[consoleplayer].mo) && (e.Thing.bISMONSTER || e.Thing.player) ) { - if ( e.Thing.IsFriend(e.DamageSource) ) + // make sure it's not a moth, because otherwise they won't shut up about accidentally hurting them (it happens a lot) + if ( e.Thing.IsFriend(e.DamageSource) && !(e.Thing is 'LampMoth') ) { if ( (!lastcombat || (gametic > lastcombat+20)) && (mutevoice.GetInt() < 1) ) lastcombat = AddOneliner("hitfriend",15); @@ -1224,13 +1441,14 @@ Class SWWMHandler : EventHandler score = int(score*(1.+.5*min(multilevel[pnum],16))); if ( !tookdamage[pnum] ) score += 100+50*spreecount[pnum]; if ( e.Thing.bBOSS ) score += 10000; + SWWMCredits.Give(e.DamageSource.player,score); + if ( e.DamageSource.player == players[consoleplayer] ) + SWWMScoreObj.Spawn(score,e.Thing.Vec3Offset(0,0,e.Thing.Height/2)); if ( level.killed_monsters+1 == level.total_monsters ) { - score += 5000; + SWWMCredits.Give(e.DamageSource.player,5000); Console.Printf(StringTable.Localize("$SWWM_LASTMONSTER"),e.DamageSource.player.GetUserName(),5000); } - SWWMCredits.Give(e.DamageSource.player,score); - // TODO floating score for HUD spreecount[pnum]++; } } @@ -1260,16 +1478,21 @@ Class SWWMHandler : EventHandler else if ( a is 'KeyCastle' ) a.SetTag("$T_KEYCASTLE"); } + // tempfix keys have no tags static void KeyTagFix( Actor a ) { let hnd = SWWMHandler(Find("SWWMHandler")); if ( hnd ) hnd.DoKeyTagFix(a); } - // tempfix keys have no tags override void WorldThingSpawned( WorldEvent e ) { - if ( e.Thing is 'Key' ) DoKeyTagFix(e.Thing); + if ( e.Thing is 'Key' ) + { + DoKeyTagFix(e.Thing); + SWWMInterest.Spawn(thekey:Key(e.Thing)); + } + if ( e.Thing.bSHOOTABLE && !(e.Thing is 'LampMoth') && !(e.Thing is 'CompanionLamp') ) SWWMCombatTracker.Spawn(e.Thing); } override void PostUiTick() @@ -1287,6 +1510,19 @@ Class SWWMHandler : EventHandler } } + override void WorldLineActivated( WorldEvent e ) + { + if ( e.Thing.player != players[consoleplayer] ) return; + // remove lines that have been already used by the local player + let ti = ThinkerIterator.Create("SWWMInterest",Thinker.STAT_USER); + SWWMInterest i; + while ( i = SWWMInterest(ti.Next()) ) + { + if ( (i.type != INT_Usable) || (i.trackedline != e.ActivatedLine) ) continue; + i.Destroy(); + } + } + override void WorldLinePreActivated( WorldEvent e ) { // oneliner on locked doors @@ -1663,6 +1899,14 @@ Class SWWMHandler : EventHandler } } + // stuff for hud + override void RenderUnderlay( RenderEvent e ) + { + if ( !statusbar || !(statusbar is 'SWWMStatusBar') ) return; + SWWMStatusBar(statusbar).viewpos = e.viewpos; + SWWMStatusBar(statusbar).viewrot = (e.viewangle,e.viewpitch,e.viewroll); + } + static void DoFlash( Actor camera, Color c, int duration ) { QueuedFlash qf = new("QueuedFlash"); diff --git a/zscript/swwm_deepdarkimpact.zsc b/zscript/swwm_deepdarkimpact.zsc index 87fcb0a24..aa8c722d6 100644 --- a/zscript/swwm_deepdarkimpact.zsc +++ b/zscript/swwm_deepdarkimpact.zsc @@ -1,12 +1,188 @@ // Dr. Locke's Mighty Wolf Breath Airgun aka "Deep Impact" Airblaster (from SWWM series) // Slot 1, replaces Fist, Staff, Hexen starting weapons +Class AirBullet : FastProjectile +{ + int tcnt; + + Default + { + Radius 2; + Height 4; + DamageFunction 100; + Speed 400; + PROJECTILE; + +FORCERADIUSDMG; + +NODAMAGETHRUST; + +RIPPER; + } + override void PostBeginPlay() + { + A_StartSound("deepimpact/bullet",CHAN_BODY,CHANF_LOOPING,1.,1.5); + } + override int DoSpecialDamage( Actor target, int damage, Name damagetype ) + { + SWWMHandler.DoKnockback(target,vel.unit(),10000); + return damage; + } + override void Effect() + { + let r = Spawn("AirBulletRing",pos); + r.angle = angle; + r.pitch = pitch; + r.roll = FRandom[Impact](0,360); + r.scale *= .3; + r.alpha *= .6; + int numpt = Random[ExploS](2,4); + for ( int i=0; i hitlist; + Array hitdist; + + override ETraceStatus TraceCallback() + { + if ( Results.HitType == TRACE_HitActor ) + { + if ( Results.HitActor == ignoreme ) return TRACE_Skip; + if ( Results.HitActor.bSHOOTABLE ) + { + hitlist.Push(Results.HitActor); + hitdist.Push(Results.Distance); + return TRACE_Skip; + } + return TRACE_Skip; + } + else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) ) + { + if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) ) + return TRACE_Stop; + return TRACE_Skip; + } + return TRACE_Stop; + } +} + +Class THitList +{ + Actor a; + int nhits; + Vector3 avgdir; + double avgdist; +} + Class DeepImpact : SWWMWeapon { int clipcount; + double charge; + bool charging; transient ui TextureID WeaponBox; - transient ui HUDFont mTewiFont; + transient ui Font TewiFont; + transient ui DynamicValueInterpolator ChargeInter; Property ClipCount : clipcount; @@ -17,9 +193,195 @@ Class DeepImpact : SWWMWeapon SWWMLoreLibrary.Add(other.player,"Locke"); } + override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) + { + //if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/DeepImpactDisplay.png",TexMan.Type_Any); + if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); + //Screen.DrawTexture(WeaponBox,false,bx-25,by-23,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-58,by-14,String.Format("Air: %3d%%",ChargeInter?ChargeInter.GetValue():clipcount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + } + + override void HudTick() + { + if ( !ChargeInter ) ChargeInter = DynamicValueInterpolator.Create(clipcount,.5,1,25); + ChargeInter.Update(clipcount); + } + override bool ReportHUDAmmo() { - return true; + return (ClipCount>0); + } + + action void A_BeginCharge() + { + invoker.charge = 0; + A_StartSound("deepimpact/charge",CHAN_WEAPONEXTRA); + A_QuakeEx(2,2,2,35,0,1,"",QF_RELATIVE|QF_SCALEUP,rollIntensity:.2); + A_AlertMonsters(100); + } + + action void A_ChargeUp() + { + invoker.charge += 1./TICRATE; + A_WeaponOffset(FRandom[Impact](-1,1)*invoker.charge,32+FRandom[Impact](-1,1)*invoker.charge); + if ( invoker.charge >= 1. ) + { + A_WeaponOffset(0,32); + player.SetPSprite(PSP_WEAPON,ResolveState("AltRelease")); + } + } + + action void A_DryFire() + { + let weap = Weapon(invoker); + if ( !weap ) return; + A_StartSound("deepimpact/dryfire",CHAN_WEAPON,CHANF_OVERLAP,.5); + A_AlertMonsters(70); + Vector3 x, y, z; + [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); + Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-3*z); + int numpt = Random[Impact](5,7); + for ( int i=0; i list; + list.Clear(); + int rings = 1; + for ( double i=0; i<1.; i+=.05 ) + { + for ( int j=0; j<360; j+=(360/rings) ) + { + dir = (x+y*cos(j)*i+z*sin(j)*i).unit(); + t.hitlist.Clear(); + t.hitdist.Clear(); + t.Trace(origin,level.PointInSector(origin.xy),dir,150-i*50,0); + for ( int i=0; i 150) || (rdir dot x < 0.7) ) continue; + m.speed = m.vel.length(); + m.vel = m.speed*(m.vel.unit()*.2+rdir).unit(); + if ( m.target == self ) continue; + if ( m.bSEEKERMISSILE ) m.tracer = m.target; + m.target = self; + } + int numpt = Random[Impact](7,12); + for ( int i=0; i=invoker.default.clipcount,"NoReload"); + XZW2 A 2 A_StartSound("deepimpact/reloadbeg",CHAN_WEAPON,CHANF_OVERLAP); + XZW3 HIJ 2; + ReloadHold: + XZW3 K 2 A_Crank(); + XZW3 LM 2; + XZW3 NOPQ 3; + XZW3 K 0 A_JumpIf((player.cmd.buttons&(BT_RELOAD|BT_ALTATTACK))&&(invoker.clipcount MainQueue, PickupQueue, FullHistory; - Actor targetactors[40], scoreactors[60], keyactors[10]; - Vector3 exitpoints[10], uselines[10]; + + Array targets; // healthbars + Array scoreobjects; // floating scores + Array interesting; // points of interest for omnisight // client cvars transient CVar safezone, maxchat[2], maxpick, chatduration, msgduration, pickduration, chatcol, teamcol, obitcol, critcol, pickcol; @@ -27,6 +29,17 @@ Class SWWMStatusBar : BaseStatusBar double FracTic; int chatopen; + // shared from renderunderlay, needed for proper interpolation of some things + Vector3 viewpos, viewrot; + + // libeye stuff + swwmLe__ProjScreen proj; + swwmLe__GLScreen gl_proj; + swwmLe__SWScreen sw_proj; + swwmLe__Viewport viewport; + bool can_project; + transient CVar cvar_renderer; + DynamicValueInterpolator HealthInter, ScoreInter, FuelInter, DashInter; override void FlushNotify() @@ -98,9 +111,59 @@ Class SWWMStatusBar : BaseStatusBar return true; } + void PrepareProjection() + { + if ( !cvar_renderer ) cvar_renderer = CVar.GetCVar("vid_rendermode",players[consoleplayer]); + if ( !cvar_renderer ) + { + can_project = proj = gl_proj; + return; + } + switch ( cvar_renderer.GetInt() ) + { + case 0: + case 1: + proj = sw_proj; + break; + default: + proj = gl_proj; + break; + } + can_project = proj; + } + + private bool CmpTarget( SWWMCombatTracker a, SWWMCombatTracker b ) + { + return ((a.mytarget && a.mytarget.player) && (!b.mytarget || !b.mytarget.player)); + } + + private bool CmpScore( SWWMScoreObj a, SWWMScoreObj b ) + { + int srt[3] = { Font.CR_GOLD, Font.CR_GREEN, Font.CR_RED }; + int s1 = 0, s2 = 0; + for ( int i=0; i<3; i++ ) + { + if ( a.tcolor == srt[i] ) s1 = i; + if ( b.tcolor == srt[i] ) s2 = i; + } + return s1 < s2; + } + + private bool CmpInterest( SWWMInterest a, SWWMInterest b ) + { + return a.type < b.type; } + + private bool CmpDist( Vector3 a, Vector3 b ) + { + double dista = level.Vec3Diff(viewpos,a).length(); + double distb = level.Vec3Diff(viewpos,b).length(); + return (dista < distb); + } + override void Tick() { Super.Tick(); + PrepareProjection(); 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]); @@ -118,8 +181,97 @@ Class SWWMStatusBar : BaseStatusBar MainQueue.Delete(i); i--; } - // update target actors + // update omnisight stuff + if ( CPlayer.mo.FindInventory("Omnisight") ) + { + interesting.Clear(); + let ii = ThinkerIterator.Create("SWWMInterest",Thinker.STAT_USER); + SWWMInterest poi; // :3 + while ( poi = SWWMInterest(ii.Next()) ) + { + if ( (poi.type == INT_Usable) && (level.Vec3Diff(viewpos,poi.pos).length() > 1000) ) continue; + interesting.Push(poi); + } + // sort by distance + for ( int i=0; i 0) && (CmpInterest(interesting[k-1],interesting[k]) || CmpDist(interesting[k-1].pos,interesting[k].pos)) ) + { + SWWMInterest tmp = interesting[k]; + interesting[k] = interesting[k-1]; + interesting[k-1] = tmp; + k--; + } + j++; + } + } + // trim size, for render performance + if ( interesting.Size() > 20 ) interesting.Resize(20); + } + // update target stuff + targets.Clear(); + let ti = ThinkerIterator.Create("SWWMCombatTracker",Thinker.STAT_USER); + SWWMCombatTracker ct; + while ( ct = SWWMCombatTracker(ti.Next()) ) + { + // ignore player unless chasecamming + if ( (ct.mytarget == players[consoleplayer].mo) && (players[consoleplayer].Camera == players[consoleplayer].mo) && !(players[consoleplayer].cheats&CF_CHASECAM) ) continue; + if ( (level.Vec3Diff(viewpos,ct.pos).length() > ((ct.mytarget&&ct.mytarget.player)?8000:1000)) ) continue; + if ( ct.mytarget && ct.mytarget.player && deathmatch ) continue; // no players in dm + if ( gametic > ct.updated+35 ) continue; + targets.Push(ct); + } + // sort by distance (give priority to players) + for ( int i=0; i 0) && (CmpTarget(targets[k-1],targets[k]) || CmpDist(targets[k-1].pos,targets[k].pos)) ) + { + SWWMCombatTracker tmp = targets[k]; + targets[k] = targets[k-1]; + targets[k-1] = tmp; + k--; + } + j++; + } + } + // trim size, for render performance + if ( targets.Size() > 40 ) targets.Resize(40); // update floating scores + scoreobjects.Clear(); + let si = ThinkerIterator.Create("SWWMScoreObj",Thinker.STAT_USER); + SWWMScoreObj so; + while ( so = SWWMScoreObj(si.Next()) ) + { + if ( level.Vec3Diff(viewpos,so.pos).length() > 2000 ) continue; + scoreobjects.Push(so); + } + // sort by distance + for ( int i=0; i 0) && (CmpScore(scoreobjects[k-1],scoreobjects[k]) || CmpDist(scoreobjects[k-1].pos,scoreobjects[k].pos)) ) + { + SWWMScoreObj tmp = scoreobjects[k]; + scoreobjects[k] = scoreobjects[k-1]; + scoreobjects[k-1] = tmp; + k--; + } + j++; + } + } + // trim size, for render performance + if ( scoreobjects.Size() > 60 ) scoreobjects.Resize(60); // update interpolators HealthInter.Update(CPlayer.health); let hnd = SWWMHandler(EventHandler.Find("SWWMHandler")); @@ -135,6 +287,9 @@ Class SWWMStatusBar : BaseStatusBar FuelInter.Update(0); DashInter.Update(0); } + // let weapons update their own interpolators + if ( CPlayer.ReadyWeapon is 'SWWMWeapon' ) + SWWMWeapon(CPlayer.ReadyWeapon).HudTick(); } override void Init() @@ -158,19 +313,121 @@ Class SWWMStatusBar : BaseStatusBar ChatTex[4] = TexMan.CheckForTexture("graphics/HUD/ChatBoxLine_Smol.png",TexMan.Type_Any); ChatTex[5] = TexMan.CheckForTexture("graphics/HUD/ChatBoxBottom_Smol.png",TexMan.Type_Any); InventoryTex = TexMan.CheckForTexture("graphics/HUD/InventoryBox.png",TexMan.Type_Any); + EnemyBTex = TexMan.CheckForTexture("graphics/HUD/EnemyBox.png",TexMan.Type_Any); + EnemyHTex = TexMan.CheckForTexture("graphics/HUD/EnemyBar.png",TexMan.Type_Any); mTewiFont = HUDFont.Create("TewiShaded"); HealthInter = DynamicValueInterpolator.Create(100,.1,1,100); ScoreInter = DynamicValueInterpolator.Create(0,.1,1,1000); FuelInter = DynamicValueInterpolator.Create(120,.5,1,100); DashInter = DynamicValueInterpolator.Create(120,.5,1,40); + gl_proj = new("swwmLe__GLScreen"); + sw_proj = new("swwmLe__SWScreen"); + PrepareProjection(); + } + + static private string FormatDist( double dist ) + { + double meters = dist/32.; + if ( meters > 1000. ) return String.Format("\cj%d\cckm",int(meters/1000.)); + return String.Format("\cj%d\ccm",int(meters)); } private void DrawTarget() { - // omnisight: usable highlights - // omnisight: key locations + viewport.FromHud(); + proj.CacheResolution(); + proj.CacheFov(players[consoleplayer].fov); + proj.Reorient(ViewPos,ViewRot); + proj.BeginProjection(); + Vector3 vdir = (cos(ViewRot.x)*cos(ViewRot.y),sin(ViewRot.x)*cos(ViewRot.y),-sin(ViewRot.y)); + // points of interest + String tag; + if ( CPlayer.mo.FindInventory("Omnisight") ) + { + for ( int i=0; i= 0 && initspd < 32 ) initspd = 32; + if ( initspd < 0 && initspd > -32 ) initspd = -32; + int boostup = 64+scoreobjects[i].seed2/2; + fo.x = (.05*initspd)*((scoreobjects[i].initialspan-(scoreobjects[i].lifespan-fractic))**.8); + fo.y = -((scoreobjects[i].initialspan-(scoreobjects[i].lifespan-fractic))**1.5)+boostup*sin((90./scoreobjects[i].initialspan)*(gametic+fractic-scoreobjects[i].starttic)); + } + else if ( scoreobjects[i].tcolor == Font.CR_GREEN ) + { + // health falls up (?) + int initspd = (128-scoreobjects[i].seed); + if ( initspd >= 0 && initspd < 32 ) initspd = 32; + if ( initspd < 0 && initspd > -32 ) initspd = -32; + int boostup = 16+scoreobjects[i].seed2/4; + fo.x = (.15*initspd)*((scoreobjects[i].initialspan-(scoreobjects[i].lifespan-fractic))**.6); + fo.y = ((scoreobjects[i].initialspan-(scoreobjects[i].lifespan-fractic))**1.2)-boostup*sin((90./scoreobjects[i].initialspan)*(gametic+fractic-scoreobjects[i].starttic)); + } + else fo.y = scoreobjects[i].initialspan-(scoreobjects[i].lifespan+fractic); // score rises linearly + Screen.DrawText(mTewiFont.mFont,scoreobjects[i].tcolor,(vpos.x-hs.x*(fo.x+mTewiFont.mFont.StringWidth(tag)/2.))/hs.x,(vpos.y-hs.y*(fo.y+(mTewiFont.mFont.GetHeight()/2.)))/hs.y,tag,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Alpha,alph); + } } private void DrawScore() diff --git a/zscript/swwm_inventory.zsc b/zscript/swwm_inventory.zsc index 8355412e7..5875252fd 100644 --- a/zscript/swwm_inventory.zsc +++ b/zscript/swwm_inventory.zsc @@ -148,6 +148,7 @@ Class SWWMHealth : Inventory abstract if ( UseSound ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6); SWWMHandler.DoFlash(Owner,Color(48,64,128,255),5); Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount); + SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),Font.CR_GREEN); return true; } @@ -402,6 +403,10 @@ Class SWWMWeapon : Weapon abstract virtual ui void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) { } + // HUD-side ticking + virtual ui void HudTick() + { + } // instant raise/lower action void A_FullRaise() { @@ -450,8 +455,20 @@ Class SWWMWeapon : Weapon abstract { double diff = deltaangle(self.angle,AngleTo(d.HitActor)); self.angle += clamp(diff,-5.,5.); - dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Melee',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); SWWMHandler.DoKnockback(d.HitActor,d.HitDir+(0,0,.2),dmg*2000); + if ( CountInv("RagekitPower") ) + { + invoker.bEXTREMEDEATH = true; + invoker.bNOEXTREMEDEATH = false; + } + else + { + invoker.bEXTREMEDEATH = false; + invoker.bNOEXTREMEDEATH = true; + } + dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Melee',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x)); + invoker.bEXTREMEDEATH = invoker.default.bEXTREMEDEATH; + invoker.bNOEXTREMEDEATH = invoker.default.bEXTREMEDEATH; if ( d.HitActor.player ) d.HitActor.A_QuakeEx(2,2,2,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.25); if ( !d.HitActor.bNOBLOOD ) { diff --git a/zscript/swwm_player.zsc b/zscript/swwm_player.zsc index b33eb499e..87a9849f6 100644 --- a/zscript/swwm_player.zsc +++ b/zscript/swwm_player.zsc @@ -33,8 +33,8 @@ Class Demolitionist : PlayerPawn Mass 500; PainChance 255; Player.DisplayName "Demolitionist"; - Player.StartItem "DeepImpact"; Player.StartItem "ExplodiumGun"; + Player.StartItem "DeepImpact"; Player.ViewHeight 52; Player.AirCapacity 0; Player.GruntSpeed 20; diff --git a/zscript/swwm_powerup.zsc b/zscript/swwm_powerup.zsc index e029e5067..a635c173c 100644 --- a/zscript/swwm_powerup.zsc +++ b/zscript/swwm_powerup.zsc @@ -15,6 +15,7 @@ Class GrilledCheeseSandwich : Inventory Owner.A_QuakeEx(9,9,9,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.); Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA); Owner.GiveBody(1000,1000); + SWWMScoreObj.Spawn(1000,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),Font.CR_GREEN); let n = Owner.FindInventory("ArmorNugget"); if ( !n ) Owner.GiveInventory("ArmorNugget",GetDefaultByType("ArmorNugget").MaxAmount); else n.Amount = n.MaxAmount; @@ -742,7 +743,7 @@ Class LampMoth : Actor override void PostBeginPlay() { Super.PostBeginPlay(); - A_StartSound("moth/fly",CHAN_BODY,CHANF_LOOPING,.15,4.,FRandom[Moth](.8,1.2)); + A_StartSound("moth/fly",CHAN_BODY,CHANF_LOOPING,.02,4.,FRandom[Moth](.8,1.2)); if ( master && master.player ) SetFriendPlayer(master.player); else bFRIENDLY = false; } @@ -1004,6 +1005,7 @@ Class CompanionLamp : Actor Default { + Tag "$T_LAMP"; +NOGRAVITY; +SHOOTABLE; +NOTELEPORT; @@ -1012,6 +1014,7 @@ Class CompanionLamp : Actor +DONTSPLASH; +INTERPOLATEANGLES; +LOOKALLAROUND; + +FRIENDLY; Radius 4; Height 16; } @@ -1119,7 +1122,7 @@ Class CompanionLamp : Actor if ( foundspot && (i > 45) ) break; } Vector3 diff = level.Vec3Diff(pos,target.pos); - if ( (diff.length() > 400) || !CheckSight(target) ) + if ( diff.length() > 400 ) { Actor f = Spawn("SWWMItemFog",pos); f.A_StartSound("lamp/disappear",CHAN_VOICE); @@ -1249,7 +1252,7 @@ Class SWWMLamp : Inventory Super.DoEffect(); if ( !thelamp ) { - thelamp = Spawn("CompanionLamp",Owner.Vec3Offset(cos(Owner.angle-180)*32,sin(Owner.angle-180)*32,Owner.height-8)); + thelamp = Spawn("CompanionLamp",Owner.Vec3Offset(cos(Owner.angle)*20,sin(Owner.angle)*20,24)); thelamp.target = Owner; thelamp.master = self; let f = Spawn("SWWMItemFog",thelamp.pos); diff --git a/zscript/swwm_splode.zsc b/zscript/swwm_splode.zsc index 00e8cb8b4..a9964aeb8 100644 --- a/zscript/swwm_splode.zsc +++ b/zscript/swwm_splode.zsc @@ -277,7 +277,7 @@ Class ExplodiumGun : SWWMWeapon int clipcount; bool chambered; transient ui TextureID WeaponBox; - transient ui HUDFont mTewiFont; + transient ui Font TewiFont; Property ClipCount : ClipCount; @@ -291,10 +291,10 @@ Class ExplodiumGun : SWWMWeapon override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) { if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/ExplodiumDisplay.png",TexMan.Type_Any); - if ( !mTewiFont ) mTewiFont = HUDFont.Create("TewiShaded"); + if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); Screen.DrawTexture(WeaponBox,false,bx-25,by-23,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - if ( chambered ) Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,bx-23,by-22,"⁺¹",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1); - Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,bx-20,by-15,String.Format("%d",max(clipcount,0)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + if ( chambered ) Screen.DrawText(TewiFont,Font.CR_FIRE,bx-23,by-22,"⁺¹",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-20,by-15,String.Format("%d",max(clipcount,0)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); } action void A_Schutt() diff --git a/zscript/swwm_tastytreat.zsc b/zscript/swwm_tastytreat.zsc index ae59b6e48..a48b0e974 100644 --- a/zscript/swwm_tastytreat.zsc +++ b/zscript/swwm_tastytreat.zsc @@ -780,7 +780,7 @@ Class CandyGun : SWWMWeapon int clipcount; bool chambered; transient ui TextureID WeaponBox; - transient ui HUDFont mTewiFont; + transient ui Font TewiFont; bool tospecial; Property ClipCount : ClipCount; @@ -795,13 +795,13 @@ Class CandyGun : SWWMWeapon override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss ) { if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/CandygunDisplay.png",TexMan.Type_Any); - if ( !mTewiFont ) mTewiFont = HUDFont.Create("TewiShaded"); + if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded'); Screen.DrawTexture(WeaponBox,false,bx-52,by-45,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - if ( chambered ) Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,bx-23,by-22,"⁺¹",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1); - Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,bx-20,by-15,String.Format("%d",max(clipcount,0)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + if ( chambered ) Screen.DrawText(TewiFont,Font.CR_FIRE,bx-23,by-22,"⁺¹",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_Spacing,-1); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-20,by-15,String.Format("%d",max(clipcount,0)),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); int cx = (Ammo1.Amount>9)?49:46; - Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,bx-cx,by-19,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); - Screen.DrawText(mTewiFont.mFont,Font.CR_FIRE,bx-38,by-41,String.Format("%d",Ammo2.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-cx,by-19,String.Format("%d",Ammo1.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); + Screen.DrawText(TewiFont,Font.CR_FIRE,bx-38,by-41,String.Format("%d",Ammo2.Amount),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true); } action void A_Schutt() @@ -964,6 +964,7 @@ Class CandyGun : SWWMWeapon override bool ReportHUDAmmo() { if ( chambered || (clipcount > 0) ) return true; + if ( Ammo1.Amount <= 0 ) return false; return Super.ReportHUDAmmo(); }