diff --git a/extras/conf/SRB2Kart2.cfg b/extras/conf/D3R-Config.cfg similarity index 75% rename from extras/conf/SRB2Kart2.cfg rename to extras/conf/D3R-Config.cfg index 97db26fc2..0482dc439 100644 --- a/extras/conf/SRB2Kart2.cfg +++ b/extras/conf/D3R-Config.cfg @@ -1,22 +1,26 @@ /*********************************************************\ vim: ai Zone Builder Game Configuration - For SRB2Kart Version 2.0 - Based on the Configuration for Sonic Robo Blast 2 Version 2.1 + For Dr. Robotnik's Ring Racers + Based on the Configuration for Sonic Robo Blast 2 Version 2.2 Contributors (alphabetical): * Foxboy + * James(/Jart) * JJames19119 * Kalaron * Kristos * MascaraSnake + * MK * Morpheus * Neo Chaotikal * Oogaland * Rob + * SeventhSentinel * Shadow Hog * SRB2-Playah * SSNTails * ST218 + * TehRealSalt * toaster * Viola \*********************************************************/ @@ -25,7 +29,7 @@ type = "Doom Builder 2 Game Configuration"; // This is the title to show for this game -game = "SRB2Jart v2.0"; +game = "Dr. Robotnik's Ring Racers"; //GZDB specific. Don't try to load lumps that don't exist. basegame = 0; @@ -126,7 +130,7 @@ skins Knuckles; Amy; Mighty; - Ray; + Motobug; Eggman; MetalSonic; Fang; @@ -158,7 +162,7 @@ generalizedsectors = true; defaultwalltexture = "GFZROCK"; defaultfloortexture = "GFZFLR01"; defaultceilingtexture = "F_SKY1"; -mixtexturesflats = false; +mixtexturesflats = true; defaulttexturescale = 1.0f; defaultflatscale = 1.0f; @@ -223,34 +227,6 @@ sprites } } -// Flat sources -flats -{ - standard1 - { - start = "F_START"; - end = "F_END"; - } - - standard2 - { - start = "FF_START"; - end = "FF_END"; - } - - standard3 - { - start = "FF_START"; - end = "F_END"; - } - - standard4 - { - start = "F_START"; - end = "FF_END"; - } -} - /* GAME DETECT PATTERN @@ -367,6 +343,34 @@ maplumpnames nodebuild = true; allowempty = true; } + + PICTURE + { + required = false; + blindcopy = true; + nodebuild = false; + } + + MINIMAP + { + required = false; + blindcopy = true; + nodebuild = false; + } + + TWEAKMAP + { + required = false; + blindcopy = true; + nodebuild = false; + } + + ENCORE + { + required = false; + blindcopy = true; + nodebuild = false; + } } scriptlumpnames @@ -656,11 +660,9 @@ linedeftypes { title = "Sector Flat Alignment"; prefix = "(7)"; - flags64text = "[6] Align only floor"; - flags2text = "[1] Align only ceiling"; - flags512text = "[9] Rotate flats"; - flags16384text = "[14] Rotate only floor"; - flags1024text = "[10] Rotate only ceiling"; + flags2048text = "[11] Don't align floor"; + flags4096text = "[12] Don't align ceiling"; + flags8192text = "[13] Use texture offsets"; } 10 @@ -681,6 +683,12 @@ linedeftypes title = "Visual Portal Between Tagged Linedefs"; prefix = "(40)"; } + + 41 + { + title = "Horizon Effect"; + prefix = "(41)"; + } 50 { @@ -706,6 +714,12 @@ linedeftypes prefix = "(80)"; } + 81 + { + title = "Block enemies"; + prefix = "(81)"; + } + 96 { title = "Apply Tag to Tagged Sectors"; @@ -769,7 +783,9 @@ linedeftypes { title = "Special Sector Properties"; prefix = "(8)"; + flags32text = "[5] Invert precipitation"; flags64text = "[6] Touch only ceiling"; + flags128text = "[7] Allow opposite gravity"; flags256text = "[8] Touch sector edge"; flags512text = "[9] Touch floor or ceiling"; } @@ -778,7 +794,12 @@ linedeftypes { title = "Chain Parameters"; prefix = "(9)"; - flags64text = "[6] Fixed spinning direction"; + flags32text = "[5] Swing instead of spin"; + flags128text = "[7] Make chain from end item"; + flags64text = "[6] Player-turnable chain"; + flags256text = "[8] Spawn link at origin"; + flags512text = "[9] Don't clip inside ground"; + flags1024text = "[10] No distance check"; } 11 @@ -796,6 +817,19 @@ linedeftypes flags64text = "[6] Randomize speed"; } + 14 + { + title = "Bustable Block Parameters"; + prefix = "(14)"; + flags32text = "[5] Particles launch from center"; + } + + 15 + { + title = "Fan Particle Spawner Parameters"; + prefix = "(15)"; + } + 64 { title = "Continuously Appearing/Disappearing FOF"; @@ -817,37 +851,42 @@ linedeftypes 20 { - title = "First Line"; + title = "PolyObject First Line"; prefix = "(20)"; - flags64text = "[6] Trigger linedef executor"; - flags128text = "[7] Intangible"; - flags256text = "[8] Stopped by pushables"; - flags512text = "[9] Render flats"; - } - - 21 - { - title = "Explicitly Include Line"; - prefix = "(21)"; } 22 { - title = "Parameters"; + title = "PolyObject Parameters"; prefix = "(22)"; + flags8text = "[3] Set translucency by X offset"; + flags32text = "[5] Render outer sides only"; + flags64text = "[6] Trigger linedef executor"; + flags128text = "[7] Intangible"; + flags256text = "[8] Stopped by pushables"; + flags512text = "[9] Render flats"; + flags8192text = "[13] Cut cyan flat pixels"; } 30 { - title = "Waving Flag"; + title = "PolyObject Waving Flag"; prefix = "(30)"; } 31 { - title = "Displacement by Front Sector"; + title = "Move PolyObject by Front Sector Displacement"; prefix = "(31)"; } + + 32 + { + title = "Rotate PolyObject by Front Sector Displacement"; + prefix = "(32)"; + flags64text = "[6] Don't turn players"; + flags512text = "[9] Turn all objects"; + } } planemove @@ -1760,7 +1799,7 @@ linedeftypes title = "Set Tagged Sector's Floor Height/Texture"; prefix = "(400)"; flags8text = "[3] Set delay by backside sector"; - flags64text = "[6] Don't change floor texture"; + flags64text = "[6] Keep floor flat"; } 401 @@ -1856,7 +1895,7 @@ linedeftypes prefix = "(403)"; flags2text = "[1] Trigger linedef executor"; flags8text = "[3] Set delay by backside sector"; - flags64text = "[6] Change floor texture"; + flags64text = "[6] Change floor flat"; } 404 @@ -1865,7 +1904,7 @@ linedeftypes prefix = "(404)"; flags2text = "[1] Trigger linedef executor"; flags8text = "[3] Set delay by backside sector"; - flags64text = "[6] Change ceiling texture"; + flags64text = "[6] Change ceiling flat"; } 405 @@ -2107,7 +2146,6 @@ linedeftypes flags8text = "[3] Set delay by backside sector"; } - 445 { title = "Make FOF Disappear/Reappear"; @@ -2116,6 +2154,45 @@ linedeftypes flags64text = "[6] Reappear"; } + 446 + { + title = "Make FOF Crumble"; + prefix = "(446)"; + flags2text = "[1] Flags determine respawn"; + flags8text = "[3] Set delay by backside sector"; + flags64text = "[6] Don't respawn"; + } + + 447 + { + title = "Change Tagged Sector's Colormap"; + prefix = "(447)"; + flags8text = "[3] Set delay by backside sector"; + flags16text = "[4] Front X/Y = Alpha"; + flags32text = "[5] Subtract Red value"; + flags64text = "[6] Subtract Green value"; + flags128text = "[7] Subtract Blue value"; + flags256text = "[8] Set relative to current"; + flags32768text = "[15] Use backside colormap"; + } + + 448 + { + title = "Change Skybox"; + prefix = "(448)"; + flags2text = "[1] Change centerpoint"; + flags8text = "[3] Set delay by backside sector"; + flags64text = "[6] For everyone"; + flags512text = "[9] Don't change viewpoint"; + } + + 449 + { + title = "Enable/Disable Waypoints"; + prefix = "(449)"; + flags64text = "[6] Enable waypoints"; + } + 450 { title = "Execute Linedef Executor (specific tag)"; @@ -2129,6 +2206,12 @@ linedeftypes prefix = "(451)"; flags8text = "[3] Set delay by backside sector"; } + + 460 + { + title = "Award Rings"; + prefix = "(460)"; + } } linedefexecpoly @@ -2201,7 +2284,7 @@ linedeftypes 488 { - title = "Move by Waypoints"; + title = "Move by Zoom Tube Waypoints"; prefix = "(488)"; flags8text = "[3] Set delay by backside sector"; flags32text = "[5] Reverse order"; @@ -2520,8 +2603,10 @@ linedeftypes prefix = "(700)"; flags2048text = "[11] No physics"; flags4096text = "[12] Not dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 1; + copyslopeargs = 1; } 701 @@ -2530,8 +2615,10 @@ linedeftypes prefix = "(701)"; flags2048text = "[11] No physics"; flags4096text = "[12] Not dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 2; + copyslopeargs = 4; } 702 @@ -2540,8 +2627,10 @@ linedeftypes prefix = "(702)"; flags2048text = "[11] No physics"; flags4096text = "[12] Not dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 3; + copyslopeargs = 5; } 703 @@ -2550,8 +2639,10 @@ linedeftypes prefix = "(703)"; flags2048text = "[11] No physics"; flags4096text = "[12] Not dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 9; + copyslopeargs = 8; } 704 @@ -2582,8 +2673,10 @@ linedeftypes prefix = "(710)"; flags2048text = "[11] No physics"; flags4096text = "[12] Not dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 4; + copyslopeargs = 2; } 711 @@ -2592,8 +2685,10 @@ linedeftypes prefix = "(711)"; flags2048text = "[11] No physics"; flags4096text = "[12] Not dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 8; + copyslopeargs = 8; } 712 @@ -2602,8 +2697,10 @@ linedeftypes prefix = "(712)"; flags2048text = "[11] No physics"; flags4096text = "[12] Not dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 12; + copyslopeargs = 10; } 713 @@ -2612,8 +2709,10 @@ linedeftypes prefix = "(713)"; flags2048text = "[11] No physics"; flags4096text = "[12] Not dynamic"; + flags32768text = "[15] Copy to other side"; slope = "regular"; slopeargs = 6; + copyslopeargs = 6; } 714 @@ -2662,6 +2761,30 @@ linedeftypes slopeargs = 3; } + 720 + { + title = "Copy Frontside Floor Slope from Line Tag"; + prefix = "(720)"; + slope = "copy"; + slopeargs = 1; + } + + 721 + { + title = "Copy Frontside Ceiling Slope from Line Tag"; + prefix = "(721)"; + slope = "copy"; + slopeargs = 2; + } + + 722 + { + title = "Copy Frontside Floor and Ceiling Slope from Line Tag"; + prefix = "(722)"; + slope = "copy"; + slopeargs = 3; + } + 723 { title = "Copy Backside Floor Slope from Line Tag"; @@ -2691,7 +2814,6 @@ linedeftypes title = "Copy Frontside Floor Slope to Backside"; prefix = "(730)"; slope = "copy"; - slopeargs = 1; copyslopeargs = 1; } @@ -2700,7 +2822,6 @@ linedeftypes title = "Copy Frontside Ceiling Slope to Backside"; prefix = "(731)"; slope = "copy"; - slopeargs = 2; copyslopeargs = 4; } @@ -2709,7 +2830,6 @@ linedeftypes title = "Copy Frontside Floor and Ceiling Slope to Backside"; prefix = "(732)"; slope = "copy"; - slopeargs = 3; copyslopeargs = 5; } @@ -2718,7 +2838,6 @@ linedeftypes title = "Copy Backside Floor Slope to Frontside"; prefix = "(733)"; slope = "copy"; - slopeargs = 1; copyslopeargs = 2; } @@ -2727,7 +2846,6 @@ linedeftypes title = "Copy Backside Ceiling Slope to Frontside"; prefix = "(734)"; slope = "copy"; - slopeargs = 2; copyslopeargs = 8; } @@ -2736,7 +2854,6 @@ linedeftypes title = "Copy Backside Floor and Ceiling Slope to Frontside"; prefix = "(735)"; slope = "copy"; - slopeargs = 3; copyslopeargs = 10; } @@ -2768,7 +2885,7 @@ linedeftypes flags2048text = "[11] No physics"; flags4096text = "[12] Dynamic"; } - + 799 { title = "Set Tagged Dynamic Slope Vertex to Front Sector Height"; @@ -2839,11 +2956,35 @@ linedeftypes title = "Fog Wall"; prefix = "(909)"; } + + 910 + { + title = "Additive Blend"; + prefix = "(910)"; + } + + 911 + { + title = "Subtractive Blend"; + prefix = "(911)"; + } + + 912 + { + title = "Reverse-Subtractive Blend"; + prefix = "(912)"; + } + + 913 + { + title = "Modulate Blend"; + prefix = "(913)"; + } } derrlines { - title = "SRB2Kart"; + title = "Ring Racers"; 2000 { @@ -2857,6 +2998,28 @@ linedeftypes prefix = "(2001)"; flags64text = "[6] Use backside"; } + + 2002 + { + title = "Per Lap Executor"; + prefix = "(2002)"; + flags2text = "[2] Every lap lower"; + flags64text = "[6] Every lap higher"; + flags512text = "[9] Activate for 1st place"; + } + + 2003 + { + title = "Death Line"; + prefix = "(2003)"; + flags64text = "[6] Use frontside"; + } + + 2004 + { + title = "Bot Trick Controller"; + prefix = "(2004)"; + } } } @@ -3014,193 +3177,6 @@ thingtypes }*/ } - enemies - { - color = 9; // Light_Blue - arrow = 1; - title = "Enemies"; - width = 24; - height = 32; - sprite = "POSSA1"; - - 100 - { - title = "Crawla (Blue)"; - sprite = "POSSA1"; - } - 101 - { - title = "Crawla (Red)"; - sprite = "SPOSA1"; - } - 102 - { - title = "Stupid Dumb Unnamed RoboFish"; - sprite = "FISHA0"; - width = 8; - height = 28; - angletext = "Jump strength"; - } - 103 - { - title = "Buzz (Gold)"; - sprite = "BUZZA1"; - width = 20; - height = 24; - flags8text = "[8] Cannot move"; - } - 104 - { - title = "Buzz (Red)"; - sprite = "RBUZA1"; - width = 20; - height = 24; - flags8text = "[8] Cannot move"; - } - 124 - { - title = "Buzz (Aqua)"; - sprite = "BBUZA1"; - width = 20; - height = 24; - } - 105 - { - title = "Jetty-Syn Bomber"; - sprite = "JETBB1"; - width = 20; - height = 48; - flags8text = "[8] Cannot move"; - } - 106 - { - title = "Jetty-Syn Gunner"; - sprite = "JETGB1"; - width = 20; - height = 48; - flags8text = "[8] Cannot move"; - } - 107 - { - title = "Crawla Commander"; - sprite = "CCOMA1"; - width = 16; - } - 108 - { - title = "Deton"; - sprite = "DETNA1"; - width = 20; - } - 109 - { - title = "Skim"; - sprite = "SKIMA1"; - width = 16; - height = 24; - } - 110 - { - title = "Turret"; - sprite = "TRETA1"; - width = 16; - height = 32; - } - 111 - { - title = "Pop-up Turret"; - sprite = "TURRI1"; - width = 12; - height = 64; - angletext = "Firing delay"; - } - 112 - { - title = "Sharp"; - sprite = "SHRPA1"; - width = 16; - height = 24; - } - 113 - { - title = "Jet Jaw"; - sprite = "JJAWA3A7"; - width = 12; - height = 20; - } - 114 - { - title = "Snailer"; - sprite = "SNLRA3A7"; - height = 48; - } - 115 - { - title = "Bird Aircraft Strike Hazard"; - sprite = "VLTRF1"; - width = 12; - height = 24; - } - 116 - { - title = "Pointy"; - sprite = "PNTYA1"; - width = 8; - height = 16; - } - 117 - { - title = "Robo-Hood"; - sprite = "ARCHA1"; - flags8text = "[8] Cannot jump"; - } - 118 - { - title = "CastleBot FaceStabber"; - sprite = "CBFSA1"; - width = 32; - height = 64; - } - 119 - { - title = "Egg Guard"; - sprite = "ESHIA1"; - width = 16; - height = 48; - flags8text = "[8] Double speed"; - } - 120 - { - title = "Green Snapper"; - sprite = "GSNPA1"; - height = 24; - } - 121 - { - title = "Minus"; - sprite = "MNUSA1"; - } - 122 - { - title = "Spring Shell (Green)"; - sprite = "SSHLA1"; - height = 40; - } - 125 - { - title = "Spring Shell (Yellow)"; - sprite = "SSHLI1"; - height = 40; - } - 123 - { - title = "Unidus"; - sprite = "UNIDA1"; - width = 18; - height = 36; - } - } - bosses { color = 8; // Dark_Gray @@ -3208,76 +3184,15 @@ thingtypes title = "Bosses"; width = 24; height = 52; - sprite = "EGGMA1"; + sprite = "internal:eggmanway"; - 200 - { - title = "Boss 1 - Egg Mobile"; - sprite = "EGGMA1"; - flags4text = "[4] End level on death"; - flags8text = "[8] Alternate laser attack"; - } - 201 - { - title = "Boss 2 - Egg Slimer"; - sprite = "EGGNA1"; - height = 48; - flags4text = "[4] End level on death"; - flags8text = "[8] Speed up when hit"; - } - 202 - { - title = "Boss 3 - Sea Egg"; - sprite = "EGGOA1"; - width = 32; - height = 80; - flags4text = "[4] End level on death"; - } - 203 - { - title = "Boss 4 - Eggscalibur"; - sprite = "EGGPA1"; - flags4text = "[4] End level on death"; - } - 207 - { - title = "Boss 5A - Metal Sonic (Race)"; - sprite = "METLI1"; - width = 16; - height = 48; - } - 208 - { - title = "Boss 5B - Metal Sonic (Battle)"; - sprite = "METLC1"; - width = 16; - height = 48; - flags4text = "[4] End level on death"; - } - 209 - { - title = "Boss 6 - Brak Eggman"; - sprite = "BRAK[1"; - width = 48; - height = 160; - flags4text = "[4] End level on death"; - flags8text = "[8] Electric barrier"; - } - 206 - { - title = "Boss ? - Brak Eggman (Old)"; - sprite = "BRAKB1"; - width = 48; - height = 160; - flags4text = "[4] End level on death"; - } 290 { arrow = 0; title = "Boss Escape Point"; width = 8; height = 16; - sprite = "internal:eggmanend"; + sprite = "internal:eggmanendy"; } 291 { @@ -3298,12 +3213,22 @@ thingtypes angletext = "No. (Sea Egg)"; flagsvaluetext = "No. (Brak)"; parametertext = "Next"; + fixedrotation = 1; } 293 { title = "Metal Sonic Gather Point"; sprite = "internal:metal"; } + 294 + { + arrow = 0; + title = "Fang Waypoint"; + flags8text = "[8] Center waypoint"; + sprite = "internal:eggmanway"; + width = 8; + height = 16; + } } rings @@ -3322,83 +3247,6 @@ thingtypes sprite = "RINGA0"; width = 16; } - 301 - { - title = "Bounce Ring"; - sprite = "internal:RNGBA0"; - } - 302 - { - title = "Rail Ring"; - sprite = "internal:RNGRA0"; - } - 303 - { - title = "Infinity Ring"; - sprite = "internal:RNGIA0"; - } - 304 - { - title = "Automatic Ring"; - sprite = "internal:RNGAA0"; - } - 305 - { - title = "Explosion Ring"; - sprite = "internal:RNGEA0"; - } - 306 - { - title = "Scatter Ring"; - sprite = "internal:RNGSA0"; - } - 307 - { - title = "Grenade Ring"; - sprite = "internal:RNGGA0"; - } - 308 - { - title = "CTF Team Ring (Red)"; - sprite = "internal:RRNGA0"; - width = 16; - } - 309 - { - title = "CTF Team Ring (Blue)"; - sprite = "internal:BRNGA0"; - width = 16; - } - 330 - { - title = "Bounce Ring Panel"; - sprite = "internal:PIKBA0"; - } - 331 - { - title = "Rail Ring Panel"; - sprite = "internal:PIKRA0"; - } - 332 - { - title = "Automatic Ring Panel"; - sprite = "internal:PIKAA0"; - } - 333 - { - title = "Explosion Ring Panel"; - sprite = "internal:PIKEA0"; - } - 334 - { - title = "Scatter Ring Panel"; - sprite = "internal:PIKSA0"; - } - 335 - { - title = "Grenade Ring Panel"; - sprite = "internal:PIKGA0"; - } } collectibles @@ -3408,204 +3256,27 @@ thingtypes width = 16; height = 32; sort = 1; - sprite = "CEMGA0"; + sprite = "ITEMALAR"; - 310 + 2000 { - title = "CTF Red Flag"; - sprite = "RFLGA0"; - width = 24; - height = 64; + title = "Random Item"; + sprite = "RNDMA0"; + width = 36; + height = 36; } - 311 + 2010 { - title = "CTF Blue Flag"; - sprite = "BFLGA0"; - width = 24; - height = 64; - } - 312 - { - title = "Special Stage Token"; - sprite = "internal:token"; - width = 8; - height = 16; - flags8height = 24; - flags4text = "[4] Mario Block version"; - flags8text = "[8] Float"; - } - 313 - { - title = "Chaos Emerald 1 (Green)"; - sprite = "EMMYA0"; - } - 314 - { - title = "Chaos Emerald 2 (Purple)"; - sprite = "EMMYB0"; - } - 315 - { - title = "Chaos Emerald 3 (Blue)"; - sprite = "EMMYC0"; - } - 316 - { - title = "Chaos Emerald 4 (Cyan)"; - sprite = "EMMYD0"; - } - 317 - { - title = "Chaos Emerald 5 (Orange)"; - sprite = "EMMYE0"; - } - 318 - { - title = "Chaos Emerald 6 (Red)"; - sprite = "EMMYF0"; - } - 319 - { - title = "Chaos Emerald 7 (Gray)"; - sprite = "EMMYG0"; - } - 320 - { - title = "Emerald Hunt Location"; - sprite = "internal:hunt"; - } - 323 - { - title = "Match Chaos Emerald Spawn"; - sprite = "CEMGA0"; - width = 8; - height = 16; - flags8height = 24; - flags8text = "[8] Float"; - } - } - - boxes - { - color = 7; // Gray - blocking = 2; - title = "Monitors"; - width = 16; - height = 32; - flags4text = "[4] Random (Strong)"; - flags8text = "[8] Random (Weak)"; - sprite = "SRBXA0"; - - 400 - { - title = "Super Ring (10 Rings)"; - sprite = "SRBXA0"; - } - 401 - { - title = "Pity Shield"; - sprite = "GRTVA0"; - } - 402 - { - title = "Attraction Shield"; - sprite = "YLTVA0"; - } - 403 - { - title = "Force Shield"; - sprite = "BLTVA0"; - } - 404 - { - title = "Armageddon Shield"; - sprite = "BKTVA0"; - } - 405 - { - title = "Whirlwind Shield"; - sprite = "WHTVA0"; - } - 406 - { - title = "Elemental Shield"; - sprite = "ELTVA0"; - } - 407 - { - title = "Super Sneakers"; - sprite = "SHTVA0"; - } - 408 - { - title = "Invincibility"; - sprite = "PINVA0"; - } - 409 - { - title = "Extra Life"; - sprite = "PRUPA0"; - flags4text = "[4] Random (Strong) / 10k points"; - flags8text = "[8] Random (Weak) / 10k points"; - } - 410 - { - title = "Eggman"; - sprite = "EGGBA0"; - flags4text = "[4] Special"; - flags8text = "[8] Ambush"; - } - 411 - { - title = "Teleporter"; - sprite = "MIXUA0"; - } - 412 - { - title = "Random"; - sprite = "QUESA0"; - flags4text = "[4] Special"; - flags8text = "[8] Ambush"; - } - 413 - { - title = "Gravity Boots"; - sprite = "GBTVA0"; - flags4text = "[4] Special"; - flags8text = "[8] Ambush"; - } - 414 - { - title = "CTF Team Ring Monitor (Red)"; - sprite = "RRBXA0"; - flags4text = "[4] Special"; - flags8text = "[8] Ambush"; - } - 415 - { - title = "CTF Team Ring Monitor (Blue)"; - sprite = "BRBXA0"; - flags4text = "[4] Special"; - flags8text = "[8] Ambush"; - } - 416 - { - title = "Recycler"; - sprite = "RECYA0"; - } - 418 - { - title = "Score (1,000 Points)"; - sprite = "PTTVA0"; - flags4text = "[4] Special"; - flags8text = "[8] Ambush"; - } - 419 - { - title = "Score (10,000 Points)"; - sprite = "PTTVF0"; - flags4text = "[4] Special"; - flags8text = "[8] Ambush"; + title = "Item Capsule"; + sprite = "ICAPA0"; + width = 56; + height = 96; + angletext = "Object Type"; + parametertext = "Amount"; + flags1text = "[1] Toggle RA persistence"; + flags4text = "[4] Add 16"; + flags8text = "[8] Double Size"; + fixedrotation = 1; } } @@ -3615,175 +3286,143 @@ thingtypes title = "Miscellaneous"; width = 16; height = 40; - sprite = "STPTA0"; + sprite = "SIGND0"; - 500 + 2424 { - title = "Air Bubble Patch"; - sprite = "BUBLA0"; - width = 8; - height = 16; - flags8text = "[8] No distance check"; + title = "Finish-Line Beam Points"; + sprite = "FLBMA0"; } 501 { + arrow = 1; title = "Level End Sign"; sprite = "SIGND0"; width = 8; height = 32; } - 502 + 3775 + { + title = "Kiosk"; + sprite = "OTCPA0"; + width = 12; + height = 48; + } + 2333 { arrow = 1; - title = "Star Post"; - sprite = "STPTA0"; - width = 64; - height = 80; - angletext = "Angle/Order"; - } - 526 - { + title = "Target Capsule"; + //sprite = "internal:kartcapsule"; + width = 28; + height = 112; blocking = 2; - title = "Cannonball"; - sprite = "CBLLA0"; - width = 20; - flags4text = "[4] Slides when pushed"; - flags8text = "[8] Not pushable"; + angletext = "Speed"; + parametertext = "Movement sequence"; + flags4text = "[4] Reverse movement"; + flags8text = "[8] Back and forth"; + fixedrotation = 1; } - 1000 + 1104 { - arrow = 1; - blocking = 2; - title = "Gargoyle"; - sprite = "GARGA1"; - flags4text = "[4] Slides when pushed"; - flags8text = "[8] Not pushable"; - } - 1102 - { - arrow = 1; - blocking = 2; - title = "Eggman Statue"; - sprite = "ESTAA1"; - width = 32; - height = 240; - flags4text = "[4] Slides when pushed"; - flags8text = "[8] Not pushable"; - } - 1106 - { - arrow = 1; - title = "Chain (Swinging)"; - sprite = "internal:chain1"; - height = 32; + title = "Mace Spawnpoint"; + sprite = "SMCEA0"; + width = 17; + height = 34; + flags4text = "[4] No sounds"; flags8text = "[8] Double size"; angletext = "Tag"; + parametertext = "Spokes"; + fixedrotation = 1; + fixedrotation = 1; + } + 1105 + { + title = "Chain with Maces Spawnpoint"; + sprite = "SMCEA0"; + width = 17; + height = 34; + flags4text = "[4] No sounds"; + flags8text = "[8] Double size"; + angletext = "Tag"; + parametertext = "Spokes"; + fixedrotation = 1; } 1107 { - arrow = 1; - title = "Chain (Spinning)"; - sprite = "internal:chain2"; - height = 32; + title = "Chain Spawnpoint"; + sprite = "BMCHA0"; + width = 17; + height = 34; flags8text = "[8] Double size"; angletext = "Tag"; + parametertext = "Spokes"; + fixedrotation = 1; } 1108 { arrow = 1; - title = "Chain (Hidden)"; + title = "Hidden Chain Spawnpoint"; sprite = "internal:chain3"; - height = 32; + width = 17; + height = 34; flags8text = "[8] Double size"; } - 1200 + 1109 { - title = "Tumbleweed (Big)"; - sprite = "BTBLA0"; - width = 24; - height = 48; - flags8text = "[8] Moves perpetually"; + title = "Firebar Spawnpoint"; + sprite = "BFBRA0"; + width = 17; + height = 34; + flags4text = "[4] No sounds"; + flags8text = "[8] Double size"; + angletext = "Tag"; + parametertext = "Spokes"; + fixedrotation = 1; } - 1201 + 1110 { - title = "Tumbleweed (Small)"; - sprite = "STBLA0"; - width = 12; - height = 24; - flags8text = "[8] Moves perpetually"; - } - 1504 - { - title = "ATZ Target"; - sprite = "RCRYB0"; - width = 24; - height = 32; - } - 1852 - { - blocking = 2; - title = "Snowman"; - sprite = "XMS3A0"; - flags4text = "[4] Slides when pushed"; - flags8text = "[8] Not pushable"; - } - 1876 - { - arrow = 1; - blocking = 2; - title = "Eggman Disco Statue"; - sprite = "ESTAB1"; - width = 20; - height = 96; - flags4text = "[4] Slides when pushed"; - flags8text = "[8] Not pushable"; + title = "Custom Mace Spawnpoint"; + sprite = "SMCEA0"; + width = 17; + height = 34; + flags4text = "[4] No sounds"; + angletext = "Tag"; + parametertext = "Spokes"; + fixedrotation = 1; } } springs { color = 12; // Light_Red - title = "Springs and Fans"; + title = "Springs and Dash Rings"; width = 48; height = 32; sprite = "RSPRD2"; - 540 - { - title = "Fan"; - sprite = "FANSA0D0"; - width = 16; - height = 16; - flags4text = "[4] Invisible"; - flags8text = "[8] No distance check"; - angletext = "Lift height"; - } - 541 - { - title = "Gas Jet"; - sprite = "STEMD0"; - width = 32; - height = 16; - } 550 { title = "Yellow Spring"; sprite = "SPVYA0"; + flags8text = "[8] Add Gravity"; } 551 { title = "Red Spring"; sprite = "SPVRA0"; + flags8text = "[8] Add Gravity"; } 552 { title = "Blue Spring"; sprite = "SPVBA0"; + flags8text = "[8] Add Gravity"; } 553 { title = "Grey Spring"; sprite = "SPVGA0"; + flags8text = "[8] Add Gravity"; } 554 { @@ -3841,92 +3480,34 @@ thingtypes sprite = "SPHGA2A8"; flags8text = "[8] Rotate 22.5° CCW"; } - } - - patterns - { - color = 5; // Magenta - arrow = 1; - title = "Special Placement Patterns"; - width = 16; - height = 384; - sprite = "RINGA0"; - - 600 + 3441 { - arrow = 0; - title = "5 Vertical Rings (Yellow Spring)"; - sprite = "RINGA0"; + title = "Dash Ring"; + sprite = "DASRA1"; + width = 32; + height = 45; + parametertext = "Strength"; + flags1text = "[1] 30 Degrees Up"; + flags4text = "[4] 60 Degrees Up"; + flags8text = "[8] Angle Upwards"; } - 601 + 3442 { - arrow = 0; - title = "5 Vertical Rings (Red Spring)"; - sprite = "RINGA0"; - height = 1024; + title = "Rainbow Ring"; + sprite = "RAIRA1"; + width = 32; + height = 45; + parametertext = "Strength"; + flags1text = "[1] 30 Degrees Up"; + flags4text = "[4] 60 Degrees Up"; + flags8text = "[8] Angle Upwards"; } - 602 + 541 { - title = "5 Diagonal Rings (Yellow Spring)"; - sprite = "RINGA0"; - height = 32; - } - 603 - { - title = "10 Diagonal Rings (Red Spring)"; - sprite = "RINGA0"; - height = 32; - } - 604 - { - title = "Circle of Rings"; - sprite = "RINGA0"; - width = 96; - height = 192; - unflippable = true; - centerHitbox = true; - } - 605 - { - title = "Circle of Rings (Big)"; - sprite = "RINGA0"; - width = 192; - unflippable = true; - centerHitbox = true; - } - 606 - { - title = "Circle of Wing Logos"; - sprite = "NWNGA0"; - width = 96; - height = 192; - unflippable = true; - centerHitbox = true; - } - 607 - { - title = "Circle of Wing Logos (Big)"; - sprite = "NWNGA0"; - width = 192; - unflippable = true; - centerHitbox = true; - } - 608 - { - title = "Circle of Rings and Wings"; - sprite = "NWNGA0"; - width = 96; - height = 192; - unflippable = true; - centerHitbox = true; - } - 609 - { - title = "Circle of Rings and Wings (Big)"; - sprite = "NWNGA0"; - width = 192; - unflippable = true; - centerHitbox = true; + title = "Gas Jet"; + sprite = "STEMD0"; + width = 32; + height = 16; } } @@ -3938,12 +3519,140 @@ thingtypes height = 16; sprite = "UNKNA0"; + 750 + { + title = "Slope Vertex"; + sprite = "internal:vertexslope"; + angletext = "Tag"; + fixedrotation = 1; + } + + 751 + { + arrow = 1; + title = "Teleport Destination"; + sprite = "internal:tele"; + } + + 752 + { + arrow = 1; + title = "Alternate View Point"; + sprite = "internal:view"; + } + + 753 + { + title = "Zoom Tube Waypoint"; + sprite = "internal:zoom"; + angletext = "Order"; + fixedrotation = 1; + } + + 754 + { + title = "Push Point"; + flags4text = "[4] Fades using XY"; + flags8text = "[8] Push using XYZ"; + sprite = "internal:tele"; + angletext = "Radius"; + fixedrotation = 1; + } + 755 + { + title = "Pull Point"; + flags4text = "[4] Fades using XY"; + flags8text = "[8] Pull using XYZ"; + sprite = "internal:tele"; + angletext = "Radius"; + fixedrotation = 1; + } + 756 + { + title = "Blast Linedef Executor"; + sprite = "internal:tele"; + width = 32; + height = 16; + angletext = "Tag"; + fixedrotation = 1; + } + 757 + { + title = "Fan Particle Generator"; + sprite = "PRTLA0"; + width = 8; + height = 16; + angletext = "Tag"; + fixedrotation = 1; + } + 758 + { + title = "Object Angle Anchor"; + sprite = "internal:view"; + } + 760 + { + title = "PolyObject Anchor"; + sprite = "internal:polyanchor"; + angletext = "ID"; + fixedrotation = 1; + } + + 761 + { + title = "PolyObject Spawn Point"; + sprite = "internal:polycenter"; + angletext = "ID"; + fixedrotation = 1; + } + + 762 + { + title = "PolyObject Spawn Point (Crush)"; + sprite = "internal:polycentercrush"; + flags4text = "[4] In-map centerpoint"; + angletext = "ID"; + fixedrotation = 1; + } + + 777 + { + title = "Floor Slope Anchor"; + sprite = "SAFA0"; + parametertext = "Level"; + } + + 778 + { + title = "Ceiling Slope Anchor"; + sprite = "SACA0"; + parametertext = "Level"; + } + + 780 + { + title = "Skybox View Point"; + sprite = "internal:skyb"; + flags4text = "[4] In-map reference point"; + angletext = "View height"; + fixedrotation = 1; + } + } + + ambience + { + color = 15; // White + title = "Ambience"; + width = 8; + height = 16; + sprite = "UNKNA0"; + 700 { title = "Water Ambience A (Large)"; sprite = "internal:ambiance"; } - + 701 { title = "Water Ambience B (Large)"; @@ -3998,94 +3707,21 @@ thingtypes sprite = "internal:ambiance"; } - 750 + 710 { - title = "Slope Vertex"; - sprite = "internal:vertexslope"; - angletext = "Tag"; + title = "Machine Ambience"; + sprite = "internal:ambiance"; } - - 751 + 735 { - arrow = 1; - title = "Teleport Destination"; - sprite = "internal:tele"; + title = "PC Merry-Go-Round Ambience"; + sprite = "internal:ambiance"; } - - 752 + 734 { - arrow = 1; - title = "Alternate View Point"; - sprite = "internal:view"; + title = "Twinkle Cart Ambience"; + sprite = "internal:ambiance"; } - - 753 - { - title = "Zoom Tube Waypoint"; - sprite = "internal:zoom"; - angletext = "Order"; - } - - 754 - { - title = "Push Point"; - flags4text = "[4] Fades using XY"; - flags8text = "[8] Push using XYZ"; - sprite = "GWLGA0"; - angletext = "Radius"; - } - 755 - { - title = "Pull Point"; - flags4text = "[4] Fades using XY"; - flags8text = "[8] Pull using XYZ"; - sprite = "GWLRA0"; - angletext = "Radius"; - } - - 760 - { - title = "PolyObject Anchor"; - sprite = "internal:polyanchor"; - angletext = "ID"; - } - - 761 - { - title = "PolyObject Spawn Point"; - sprite = "internal:polycenter"; - angletext = "ID"; - } - - 762 - { - title = "PolyObject Spawn Point (Crush)"; - sprite = "internal:polycentercrush"; - angletext = "ID"; - } - - 777 - { - title = "Floor Slope Anchor"; - sprite = "internal:Vertex_Anchor_Floor.png"; - parametertext = "Level"; - } - - 778 - { - title = "Ceiling Slope Anchor"; - sprite = "internal:Ceiling_Anchor_Floor.png"; - parametertext = "Level"; - } - - 780 - { - title = "Skybox View Point"; - sprite = "internal:skyb"; - flags4text = "[4] In-map reference point"; - angletext = "View height"; - } - } hazards @@ -4104,59 +3740,58 @@ thingtypes flags8height = 24; flags8text = "[8] Float"; } + 522 + { + title = "Wall Spike"; + sprite = "WSPKALAR"; + width = 16; + height = 14; + arrow = 1; + flags1text = "[1] Start retracted"; + flags4text = "[4] Retractable"; + flags8text = "[8] Intangible"; + parametertext = "Start delay"; + } 523 { title = "Spike"; sprite = "USPKA0"; width = 8; - height = 42; + height = 32; + flags1text = "[1] Start retracted"; flags4text = "[4] Retractable"; - flags8text = "[8] Solid"; + flags8text = "[8] Intangible"; angletext = "Retraction interval"; + fixedrotation = 1; + parametertext = "Start delay"; } - 524 + 1130 { - arrow = 1; - title = "Big Floating Mine"; - width = 16; - height = 32; - sprite = "BMNEA1"; + title = "Small Mace"; + sprite = "SMCEA0"; + width = 17; + height = 34; } - 527 + 1131 { - arrow = 1; - title = "Big Floating Mine (Air)"; - width = 16; - height = 32; - sprite = "BMNEA1"; + title = "Big Mace"; + sprite = "BMCEA0"; + width = 34; + height = 68; } - 525 + 1136 { - title = "Cannonball Launcher"; - sprite = "internal:cannonball"; + title = "Small Fireball"; + sprite = "SFBRA0"; + width = 17; + height = 34; } - 1101 + 1137 { - title = "Torch"; - sprite = "FLAMA0"; - width = 8; - height = 32; - } - 1105 - { - title = "Mace (Swinging)"; - sprite = "internal:mace1"; - flags4text = "[4] No sounds"; - flags8text = "[8] Double size"; - angletext = "Tag"; - } - 1104 - { - title = "Mace (Spinning)"; - sprite = "internal:mace2"; - flags4text = "[4] No sounds"; - flags8text = "[8] Double size"; - angletext = "Tag"; + title = "Large Fireball"; + sprite = "BFBRA0"; + width = 34; + height = 68; } 1202 { @@ -4164,6 +3799,7 @@ thingtypes title = "Rock Spawner"; sprite = "ROIAA0"; angletext = "Tag"; + fixedrotation = 1; } 1300 { @@ -4174,6 +3810,7 @@ thingtypes flags8text = "[8] Waves vertically"; angletext = "On/Off time"; parametertext = "Strength"; + fixedrotation = 1; } 1301 { @@ -4183,50 +3820,7 @@ thingtypes flags8text = "[8] Shoot downwards"; angletext = "On/Off time"; parametertext = "Strength"; - } - 1500 - { - arrow = 1; - blocking = 2; - title = "Trapgoyle"; - sprite = "GARGA1"; - width = 16; - height = 40; - flags4text = "[4] Slides when pushed"; - flags8text = "[8] Not pushable"; - } - 1501 - { - arrow = 1; - blocking = 2; - title = "Trapgoyle (Up)"; - sprite = "GARGA1"; - width = 16; - height = 40; - flags4text = "[4] Slides when pushed"; - flags8text = "[8] Not pushable"; - } - 1502 - { - arrow = 1; - blocking = 2; - title = "Trapgoyle (Down)"; - sprite = "GARGA1"; - width = 16; - height = 40; - flags4text = "[4] Slides when pushed"; - flags8text = "[8] Not pushable"; - } - 1503 - { - arrow = 1; - blocking = 2; - title = "Trapgoyle (Long)"; - sprite = "GARGA1"; - width = 16; - height = 40; - flags4text = "[4] Slides when pushed"; - flags8text = "[8] Not pushable"; + fixedrotation = 1; } 3576 { @@ -4250,15 +3844,6 @@ thingtypes height = 40; sprite = "FWR1A0"; - 757 - { - title = "Fan Particle Generator"; - sprite = "PRTLA0"; - width = 8; - height = 16; - angletext = "Particle speed"; - parametertext = "Interval"; - } 800 { title = "GFZ Flower"; @@ -4319,27 +3904,7 @@ thingtypes height = 16; hangs = 1; angletext = "Dripping interval"; - } - 1003 - { - title = "Coral (Green)"; - sprite = "CRL1A0"; - width = 8; - height = 16; - } - 1004 - { - title = "Coral (Red)"; - sprite = "CRL2A0"; - width = 8; - height = 16; - } - 1005 - { - title = "Coral (Orange)"; - sprite = "CRL3A0"; - width = 8; - height = 16; + fixedrotation = 1; } 1006 { @@ -4456,104 +4021,6 @@ thingtypes } } - nights - { - color = 13; // Pink - title = "NiGHTS Items"; - width = 12; - height = 32; - sprite = "NWNGA0"; - - 1703 - { - title = "Ideya Drone"; - sprite = "NDRNA1"; - width = 16; - height = 56; - flags8text = "[8] Die upon time up"; - angletext = "Time limit"; - } - 1704 - { - arrow = 1; - title = "Bumper"; - sprite = "NBMPG3G7"; - width = 32; - height = 64; - unflippable = true; - flagsvaluetext = "Pitch"; - angletext = "Yaw"; - } - 1705 - { - arrow = 1; - title = "Hoop (Generic)"; - sprite = "HOOPA0"; - width = 80; - height = 160; - unflippable = true; - centerHitbox = true; - flagsvaluetext = "Height"; - angletext = "Pitch/Yaw"; - } - 1706 - { - title = "Wing Logo"; - sprite = "NWNGA0"; - height = 24; - unflippable = true; - } - 1707 - { - title = "Super Paraloop"; - sprite = "NPRUA0"; - flags4text = "[4] Bonus time only"; - flags8text = "[8] Spawn immediately"; - } - 1708 - { - title = "Drill Refill"; - sprite = "NPRUB0"; - flags4text = "[4] Bonus time only"; - flags8text = "[8] Spawn immediately"; - } - 1709 - { - title = "Nightopian Helper"; - sprite = "NPRUC0"; - flags4text = "[4] Bonus time only"; - flags8text = "[8] Spawn immediately"; - } - 1711 - { - title = "Extra Time"; - sprite = "NPRUD0"; - flags4text = "[4] Bonus time only"; - flags8text = "[8] Spawn immediately"; - } - 1712 - { - title = "Link Freeze"; - sprite = "NPRUE0"; - flags4text = "[4] Bonus time only"; - flags8text = "[8] Spawn immediately"; - } - 1713 - { - arrow = 1; - title = "Hoop (Customizable)"; - flags1text = "[1] Radius +16"; - flags2text = "[2] Radius +32"; - flags4text = "[4] Radius +64"; - flags8text = "[8] Radius +128"; - sprite = "HOOPA0"; - width = 80; - height = 160; - unflippable = true; - centerHitbox = true; - } - } - nightstrk { color = 13; // Pink @@ -4572,6 +4039,7 @@ thingtypes flagsvaluetext = "Order"; angletext = "Radius/Direction"; parametertext = "Mare"; + fixedrotation = 1; } 1701 { @@ -4591,216 +4059,6 @@ thingtypes flagsvaluetext = "Order"; parametertext = "Mare"; } - 1710 - { - title = "Ideya Capture"; - sprite = "CAPSA0"; - width = 72; - height = 144; - angletext = "Rings"; - parametertext = "Mare"; - } - } - - mario - { - color = 6; // Brown - title = "Mario Items"; - width = 16; - height = 32; - sprite = "GOOMA0"; - - 1800 - { - title = "Coin"; - sprite = "COINA0"; - height = 24; - flags8height = 24; - flags8text = "[8] Float"; - } - 1801 - { - arrow = 1; - title = "Goomba"; - sprite = "GOOMA0"; - width = 24; - } - 1802 - { - arrow = 1; - title = "Goomba (Blue)"; - sprite = "BGOMA0"; - width = 24; - } - 1803 - { - title = "Fire Flower"; - sprite = "FFWRB0"; - } - 1804 - { - title = "Koopa Shell"; - sprite = "SHLLA0"; - width = 8; - height = 16; - } - 1805 - { - title = "Puma (Jumping Fireball)"; - sprite = "PUMAA0"; - width = 8; - height = 16; - angletext = "Jump strength"; - } - 1806 - { - title = "King Bowser"; - sprite = "KOOPA0"; - height = 28; - } - 1807 - { - title = "Axe"; - sprite = "MAXEA0"; - width = 8; - height = 16; - } - 1808 - { - title = "Bush (Short)"; - sprite = "MUS1A0"; - } - 1809 - { - title = "Bush (Tall)"; - sprite = "MUS2A0"; - } - 1810 - { - title = "Toad"; - sprite = "TOADA0"; - width = 8; - } - } - - srb1 - { - color = 3; // Cyan - arrow = 1; - title = "SRB1 Remake"; - width = 20; - height = 32; - sprite = "SRBAA1"; - - 4000 - { - title = "SRB1 Crawla"; - sprite = "SRBAA1"; - height = 40; - } - 4001 - { - title = "GuardRobo"; - sprite = "SRBBA1"; - width = 17; - height = 40; - } - 4002 - { - title = "Pyrin"; - sprite = "SRBCB1"; - width = 22; - } - 4003 - { - title = "HotRobo"; - sprite = "SRBDA0"; - height = 40; - } - 4004 - { - title = "Pogminz"; - sprite = "SRBEA1"; - } - 4005 - { - title = "Pogminz (Water)"; - sprite = "SRBEA1"; - } - 4006 - { - title = "Pog-GX2"; - sprite = "SRBFA0"; - width = 10; - height = 34; - } - 4007 - { - title = "Pyrex"; - sprite = "SRBGA1"; - width = 24; - } - 4008 - { - title = "SRB1 Turret"; - sprite = "SRBHA0"; - width = 24; - hangs = 1; - } - 4009 - { - title = "SWAT Bot"; - sprite = "SRBIA1"; - width = 21; - height = 69; - } - 4010 - { - title = "SpyBot 2000"; - sprite = "SRBJA0"; - width = 36; - height = 62; - } - 4011 - { - title = "Buzz Bomber"; - sprite = "SRBKA0"; - width = 44; - height = 45; - } - 4012 - { - arrow = 0; - title = "RBZ Spike"; - sprite = "SRBLA0"; - width = 10; - height = 53; - } - 4013 - { - arrow = 0; - blocking = 2; - title = "Dumb Metal Sonic"; - sprite = "SRBMC0"; - width = 16; - height = 40; - flags4text = "[4] Slides when pushed"; - flags8text = "[8] Not pushable"; - } - 4014 - { - title = "Super SWAT Bot"; - sprite = "SRBNA1"; - width = 21; - height = 69; - } - 4015 - { - title = "Genrex"; - sprite = "SRBOA1"; - width = 17; - height = 40; - } } bsz @@ -4900,36 +4158,6 @@ thingtypes title = "Short Flower (Orange)"; sprite = "BSZ3F0"; } - 1430 - { - title = "Tulip (Red)"; - sprite = "BSZ4A0"; - } - 1431 - { - title = "Tulip (Purple)"; - sprite = "BSZ4B0"; - } - 1432 - { - title = "Tulip (Blue)"; - sprite = "BSZ4C0"; - } - 1433 - { - title = "Tulip (Cyan)"; - sprite = "BSZ4D0"; - } - 1434 - { - title = "Tulip (Yellow)"; - sprite = "BSZ4E0"; - } - 1435 - { - title = "Tulip (Orange)"; - sprite = "BSZ4F0"; - } 1440 { title = "Cluster (Red)"; @@ -5046,29 +4274,17 @@ thingtypes { color = 4; // Red arrow = 1; - title = "SRB2Kart Stuff"; - sprite = "ITEMALAR"; + title = "Ring Racers Stuff"; + sprite = "AUDIA2A8"; width = 8; height = 16; - 2000 + 4095 { - title = "Random Item"; - sprite = "RNDMA0"; - width = 36; - height = 36; - } - 2333 - { - title = "Capsule"; - //sprite = "internal:kartcapsule"; - width = 28; - height = 112; - blocking = 2; - angletext = "Speed"; - parametertext = "Movement sequence"; - flags4text = "[4] Reverse movement"; - flags8text = "[8] Back and forth"; + title = "Empty Kart"; + sprite = "KARTA2A8"; + width = 30; + height = 30; } 1488 { @@ -5078,13 +4294,6 @@ thingtypes width = 8; height = 20; } - 1479 - { - title = "Torch (no fullbright)"; - sprite = "FLAMA0"; - width = 8; - height = 32; - } 1480 { blocking = 2; @@ -5406,6 +4615,30 @@ thingtypes width = 5; height = 204; } + 691 + { + title = "FPZ FrostThrower"; + sprite = "SFTRB0"; + width = 32; + height = 45; + flags1text = "[1] Max Range"; + flags8text = "[8] Alternate timming"; + angletext = "Delay"; + } + 693 + { + title = "FPZ Side-FrostThrower"; + sprite = "SFTRB0"; + width = 32; + height = 45; + } + 3456 + { + title = "SSZ Cloud Cluster"; + sprite = "SSCLA0"; + width = 48; + height = 45; + } 717 { title = "HTZ Bush"; @@ -5767,16 +5000,6 @@ thingtypes width = 16; height = 64; } - 735 - { - title = "PC Merry-Go-Round Ambience"; - sprite = "internal:ambiance"; - } - 734 - { - title = "Twinkle Cart Ambience"; - sprite = "internal:ambiance"; - } 733 { title = "PC Exploding Barrel"; @@ -5902,35 +5125,40 @@ thingtypes 2001 { title = "Waypoint (height = next waypoint ID)"; - sprite = "EMBMP0"; + sprite = "WAY1A0"; angletext = "ID"; flags1text = "[1] Disable"; - flags4text = "[4] Shortcut"; + flags4text = "[4] On shortcut"; flags8text = "[8] No respawn"; heighttext = "Next"; + parametertext = "Finish?"; + fixedrotation = 1; } 2002 { title = "Waypoint Riser"; - sprite = "EMBMY0"; + sprite = "WAY3A0"; angletext = "ID (if flag [4] set)"; flags2text = "[2] Look for FOF above"; flags4text = "[4] Require matching waypoint ID"; flags8text = "[8] Copy exact height"; + fixedrotation = 1; } 2003 { title = "Waypoint Anchor"; - sprite = "EMBMR0"; + sprite = "WAY2A0"; angletext = "ID"; + fixedrotation = 1; } 2004 { title = "Bot Hint"; - sprite = "EMBMA0"; + sprite = "WAY4A0"; angletext = "Radius"; flags8text = "[8] Avoid this area"; parametertext = "Strength (default = 2)"; + fixedrotation = 1; } } } @@ -6006,333 +5234,3 @@ thingsfilters type = 292; } } - -texturesets -{ - - set0 - { - name = "Greenflower Zone"; - filter0 = "GFZ*"; - filter1 = "FWATER*"; - filter2 = "CFALL*"; - filter3 = "GFALL*"; - filter4 = "FLOOR0_4"; - filter5 = "FLOOR1_2"; - filter6 = "FLOOR1_3"; - filter7 = "BWATER*"; - filter8 = "FLOOR0_2"; - filter9 = "DEM1_5"; - filter10 = "OLDROCKW"; - filter11 = "ROCKY*"; - filter12 = "OWOODW"; - filter13 = "WOODFLR"; - } - - set1 - { - name = "Techno Hill Zone"; - filter0 = "TH*"; - filter1 = "CHEMG*"; - filter2 = "TFALL?"; - filter3 = "ALTBOX*"; - filter4 = "BOXWARN?"; - filter5 = "PIPE*"; - filter6 = "PISTON"; - filter7 = "WHZ*"; - filter8 = "BAR*"; - filter9 = "COMP?"; - filter10 = "INFOWAL?"; - filter11 = "SPECWLL?"; - filter12 = "SUPPORT?"; - filter13 = "LITE*"; - filter14 = "BLUEW?"; - filter15 = "GREYW?"; - filter16 = "RED?"; - filter17 = "YEL?"; - filter18 = "SLITE?"; - filter19 = "DOORTRK1"; - filter20 = "GOOP*"; - filter21 = "AFALL?"; - filter22 = "STEEL*"; - filter23 = "CONVEY?"; - } - - set2 - { - name = "Deep Sea Zone"; - filter0 = "DSZ*"; - filter1 = "LWATER*"; - filter2 = "LFALL?"; - filter3 = "BFALL?"; - filter4 = "DEEPSE*"; - filter5 = "WEB?"; - filter6 = "RUINS1_1"; - filter7 = "CLRWAL*"; - } - - set3 - { - name = "Castle Eggman Zone"; - filter0 = "CASTLE*"; - filter1 = "STONE*"; - filter2 = "DMA*"; - filter3 = "LIB*"; - filter4 = "CEZ*"; - filter5 = "CEFLAG*"; - filter6 = "TOMBST?"; - filter7 = "NCEZW?"; - filter8 = "DEM1_1"; - filter9 = "WEBB?"; - filter10 = "LFZ*"; - filter11 = "WEEDWALL"; - filter12 = "STORM1"; - filter13 = "STR_M1"; - } - - set4 - { - name = "Arid Canyon Zone"; - filter0 = "AC*"; - filter1 = "OIL*"; - filter2 = "SOLFALL?"; - } - - set5 - { - name = "Red Volcano Zone"; - filter0 = "RVZ*"; - filter1 = "D2LAVA"; - filter2 = "MM*"; - filter3 = "ROCKFLR*"; - filter4 = "ROCKWLL*"; - filter5 = "DLAVA?"; - filter6 = "LAVA?"; - filter7 = "RLAVA?"; - filter8 = "SFALL?"; - filter9 = "MINE*"; - filter10 = "LVASAND?"; - filter11 = "FLOOR6_2"; - filter12 = "TLITE6_1"; - filter13 = "ROCKBOIL"; - filter14 = "TLITE6_5"; - filter15 = "TLITE6_6"; - filter16 = "FLOOR6_1"; - } - - set6 - { - name = "Dark City Zone"; - filter0 = "DC*"; - filter1 = "SIDEWALK"; - filter2 = "BRICK*"; - filter3 = "CEMENT?"; - filter4 = "BRIDGE*"; - filter5 = "CATFLR02"; - filter6 = "LIFT*"; - } - - set7 - { - name = "Doomship Zone"; - filter0 = "DSHIP*"; - } - - set8 - { - name = "Egg Rock Zone"; - filter0 = "ER*"; - filter1 = "MEK*"; - filter2 = "VENT*"; - filter3 = "DOWN*"; - filter4 = "UP*"; - filter5 = "LITE*"; - filter6 = "BLUEW?"; - filter7 = "GREYW?"; - filter8 = "RED?"; - filter9 = "YEL?"; - filter10 = "EGRID*"; - filter11 = "CONVEY?"; - } - - set9 - { - name = "Mario"; - filter0 = "MARFLAG?"; - filter1 = "THWOMP*"; - filter2 = "PTZ*"; - filter3 = "MARIO*"; - filter4 = "MARROCK?"; - } - - set10 - { - name = "Special Stage"; - filter0 = "ASPEC*"; - filter1 = "CSPEC*"; - filter2 = "EMTEX?"; - filter3 = "GSPEC*"; - filter4 = "PSPEC*"; - filter5 = "RSPEC*"; - filter6 = "SFLR*"; - filter7 = "SPACE*"; - filter8 = "SPC*"; - filter9 = "SPEC*"; - filter10 = "VOID*"; - filter11 = "WSPEC*"; - filter12 = "YSPEC*"; - filter13 = "BSPEC*"; - } - - set11 - { - name = "Multiplayer levels"; - filter0 = "ALLYRING"; - filter1 = "BRAKCAT?"; - filter2 = "SANDWLL"; - filter3 = "LAKE*"; - filter4 = "AP*"; - filter5 = "ZIM*"; - filter6 = "RCZ*"; - } - - set12 - { - name = "Caves and Cliffs"; - filter0 = "CAVE*"; - filter1 = "ROCK?"; - filter2 = "ROCKF?"; - filter3 = "CLIFF*"; - filter4 = "SHAL*"; - filter5 = "MRNR*"; - } - - set13 - { - name = "Christmas"; - filter0 = "XM*"; - filter1 = "FROST*"; - filter2 = "GRSWALL3"; - filter3 = "GRSWALL4"; - } - - set14 - { - name = "Forts"; - filter0 = "2FORT*"; - } - - set15 - { - name = "Ice and Snow"; - filter0 = "ICE*"; - filter1 = "SNOW*"; - } - - set16 - { - name = "Jungle"; - filter0 = "GRSEDG?"; - filter1 = "JNG*"; - filter2 = "SHROOM*"; - } - - set17 - { - name = "Sand"; - filter0 = "SAND"; - filter1 = "SANDW"; - filter2 = "SANDFLR*"; - filter3 = "SND*"; - filter4 = "AGZ*"; - filter5 = "QUIK*"; - filter6 = "Q?FALL?"; - } - - set18 - { - name = "Forest"; - filter0 = "WOOD*"; - filter1 = "EC*"; - filter2 = "GRASS1*"; - filter3 = "VFZ*"; - filter4 = "OWOODW"; - filter5 = "DEM1_2"; - } - - set19 - { - name = "Generic"; - filter0 = "BUST*"; - filter1 = "CEIL3_1"; - filter2 = "CTFFLG*"; - filter3 = "DEM1_3"; - filter4 = "BIGX"; - filter5 = "ARROW?"; - filter6 = "HAZARD?"; - filter7 = "*PIT"; - filter8 = "SPIKE*"; - filter9 = "*CAUTN"; - filter10 = "TRAPFLR"; - filter11 = "EGGTRAP?"; - filter12 = "GLASS*"; - filter13 = "DIRT*"; - filter14 = "EGGOLD*"; - filter15 = "MARE*"; - filter16 = "BLUEFLR"; - filter17 = "BLUWALL"; - filter18 = "CYAN*"; - filter19 = "GOLD*"; - filter20 = "GREENFLR"; - filter21 = "GRNWALL"; - filter22 = "GREYFLR"; - filter23 = "GRYWALL"; - filter24 = "LIME*"; - filter25 = "NEOG*"; - filter26 = "ORGFLR"; - filter27 = "ORANGE"; - filter28 = "PUR*"; - filter29 = "REDFLR"; - filter30 = "REDWALL"; - filter31 = "VIO*"; - filter32 = "YELFLR"; - filter33 = "YELWALL"; - } - - set20 - { - name = "Unsorted"; - filter0 = "FLAT1_2"; - filter1 = "FOSSIL*"; - filter2 = "F_METAL?"; - filter3 = "GRASSY*"; - filter4 = "WATER?"; - filter5 = "SURF*"; - filter6 = "OFALL?"; - filter7 = "MFALL1"; - filter8 = "SCREEN01"; - filter9 = "TRCKWLL1"; - filter10 = "WINDOW"; - filter11 = "XTRMCHK*"; - filter12 = "CHAINFEN"; - filter13 = "SRB1*"; - filter14 = "DISCO*"; - filter15 = "DANCE?"; - filter16 = "RECORD"; - filter17 = "DFZ*"; - } - - set21 - { - name = "Classic levels"; - filter0 = "GHZ*"; - filter1 = "HPZ*"; - filter2 = "LBZ*"; - filter3 = "GRASS2"; - filter4 = "GRASS3"; - filter5 = "GRSWALL"; - filter6 = "GRNLITE1"; - filter7 = "GATE2"; - filter8 = "DEM1_6"; - } -} diff --git a/src/command.c b/src/command.c index 3c5cb9180..dd09846a9 100644 --- a/src/command.c +++ b/src/command.c @@ -2085,42 +2085,6 @@ void CV_AddValue(consvar_t *var, INT32 increment) if (var->PossibleValue) { - /* - if (var == &cv_nextmap) - { - // Special case for the nextmap variable, used only directly from the menu - INT32 oldvalue = var->value - 1, gt; - gt = cv_newgametype.value; - { - newvalue = var->value - 1; - do - { - if(increment > 0) // Going up! - { - if (++newvalue == NUMMAPS) - newvalue = -1; - } - else // Going down! - { - if (--newvalue == -2) - newvalue = NUMMAPS-1; - } - - if (newvalue == oldvalue) - break; // don't loop forever if there's none of a certain gametype - - if(!mapheaderinfo[newvalue]) - continue; // Don't allocate the header. That just makes memory usage skyrocket. - - } while (!M_CanShowLevelInList(newvalue, gt)); - - var->value = newvalue + 1; - var->func(); - return; - } - } - else - */ #define MINVAL 0 #define MAXVAL 1 if (var->PossibleValue[MINVAL].strvalue && !strcmp(var->PossibleValue[MINVAL].strvalue, "MIN")) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1e5787583..03ec45c5b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -933,7 +933,6 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) CopyCaretColors(netbuffer->u.serverinfo.servername, cv_servername.string, MAXSERVERNAME); - strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7); M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16); diff --git a/src/d_clisrv.h b/src/d_clisrv.h index ca36ba26c..ab220707b 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -279,7 +279,6 @@ typedef struct tic_t time; tic_t leveltime; char servername[MAXSERVERNAME]; - char mapname[8]; char maptitle[33]; unsigned char mapmd5[16]; UINT8 actnum; diff --git a/src/d_main.c b/src/d_main.c index d6dcfbcab..7e3843b8e 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -353,7 +353,7 @@ static void D_Display(void) if (gamestate != GS_LEVEL && rendermode != render_none) { V_SetPaletteLump("PLAYPAL"); // Reset the palette - R_ReInitColormaps(0, LUMPERROR); + R_ReInitColormaps(0, NULL, 0); } F_WipeStartScreen(); @@ -939,20 +939,16 @@ void D_StartTitle(void) if (netgame) { - if (gametyperules & GTR_CAMPAIGN) + G_SetGamestate(GS_WAITINGPLAYERS); // hack to prevent a command repeat + + if (server) { - G_SetGamestate(GS_WAITINGPLAYERS); // hack to prevent a command repeat + i = G_GetFirstMapOfGametype(gametype)+1; - if (server) - { - char mapname[6]; + if (i > nummapheaders) + I_Error("D_StartTitle: No valid map ID found!?"); - strlcpy(mapname, G_BuildMapName(spstage_start), sizeof (mapname)); - strlwr(mapname); - mapname[5] = '\0'; - - COM_BufAddText(va("map %s\n", mapname)); - } + COM_BufAddText(va("map %s\n", G_BuildMapName(i))); } return; @@ -1196,13 +1192,10 @@ D_ConvertVersionNumbers (void) // void D_SRB2Main(void) { - INT32 i; - UINT16 wadnum; - lumpinfo_t *lumpinfo; - char *name; - INT32 p; + INT32 numbasemapheaders; + INT32 pstartmap = 1; boolean autostart = false; @@ -1445,31 +1438,16 @@ void D_SRB2Main(void) #endif //ifndef DEVELOP - // - // search for maps - // - for (wadnum = 0; wadnum <= mainwads; wadnum++) - { - lumpinfo = wadfiles[wadnum]->lumpinfo; - for (i = 0; i < wadfiles[wadnum]->numlumps; i++, lumpinfo++) - { - name = lumpinfo->name; + // Do it before P_InitMapData because PNG patch + // conversion sometimes needs the palette + V_ReloadPalette(); - if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers - { - INT16 num; - if (name[5] != '\0') - continue; - num = (INT16)M_MapNumber(name[3], name[4]); + // + // search for mainwad maps + // + P_InitMapData(0); - // we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant - if (num <= NUMMAPS && mapheaderinfo[num - 1]) - { - mapheaderinfo[num - 1]->alreadyExists = true; - } - } - } - } + numbasemapheaders = nummapheaders; CON_SetLoadingProgress(LOADED_IWAD); @@ -1478,37 +1456,9 @@ void D_SRB2Main(void) D_CleanFile(startuppwads); // - // search for maps... again. + // search for pwad maps // - for (wadnum = mainwads+1; wadnum < numwadfiles; wadnum++) - { - lumpinfo = wadfiles[wadnum]->lumpinfo; - for (i = 0; i < wadfiles[wadnum]->numlumps; i++, lumpinfo++) - { - name = lumpinfo->name; - - if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers - { - INT16 num; - if (name[5] != '\0') - continue; - num = (INT16)M_MapNumber(name[3], name[4]); - - // we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant - if (num <= NUMMAPS && mapheaderinfo[num - 1]) - { - if (mapheaderinfo[num - 1]->alreadyExists != false) - { - G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you - } - - mapheaderinfo[num - 1]->alreadyExists = true; - } - - CONS_Printf("%s\n", name); - } - } - } + P_InitMapData(numbasemapheaders); CON_SetLoadingProgress(LOADED_PWAD); @@ -1765,14 +1715,14 @@ void D_SRB2Main(void) // rei/miru: bootmap (Idea: starts the game on a predefined map) if (bootmap && !(M_CheckParm("-warp") && M_IsNextParm())) { - pstartmap = bootmap; + pstartmap = G_MapNumber(bootmap)+1; - if (pstartmap < 1 || pstartmap > NUMMAPS) - I_Error("Cannot warp to map %d (out of range)\n", pstartmap); - else + if (pstartmap > nummapheaders) { - autostart = true; + I_Error("Cannot warp to map %s (not found)\n", bootmap); } + + autostart = true; } // Has to be done before anything else so skin, color, etc in command buffer has an affect. @@ -1869,14 +1819,11 @@ void D_SRB2Main(void) if (server && !M_CheckParm("+map")) { - // Prevent warping to nonexistent levels - if (W_CheckNumForName(G_BuildMapName(pstartmap)) == LUMPERROR) - I_Error("Could not warp to %s (map not found)\n", G_BuildMapName(pstartmap)); // Prevent warping to locked levels // ... unless you're in a dedicated server. Yes, technically this means you can view any level by // running a dedicated server and joining it yourself, but that's better than making dedicated server's // lives hell. - else if (!dedicated && M_MapLocked(pstartmap)) + if (!dedicated && M_MapLocked(pstartmap)) I_Error("You need to unlock this level before you can warp to it!\n"); else { diff --git a/src/d_net.c b/src/d_net.c index 201920f73..4500057e9 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -911,9 +911,9 @@ static void DebugPrintpacket(const char *header) netbuffer->u.servercfg.modifiedgame); break; case PT_SERVERINFO: - fprintf(debugfile, " '%s' player %d/%d, map %s, filenum %d, time %u \n", + fprintf(debugfile, " '%s' player %d/%d, filenum %d, time %u \n", netbuffer->u.serverinfo.servername, netbuffer->u.serverinfo.numberofplayer, - netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.mapname, + netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.fileneedednum, (UINT32)LONG(netbuffer->u.serverinfo.time)); fprintfstringnewline((char *)netbuffer->u.serverinfo.fileneeded, diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c0057896a..fdd6e75e4 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1394,7 +1394,7 @@ UINT8 CanChangeSkin(INT32 playernum) return true; // Force skin in effect. - if ((cv_forceskin.value != -1) || (mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0')) + if (cv_forceskin.value != -1) return false; // Can change skin in intermission and whatnot. @@ -2510,8 +2510,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r if (delay != 2) { UINT8 flags = 0; - const char *mapname = G_BuildMapName(mapnum); - I_Assert(W_CheckNumForName(mapname) != LUMPERROR); + //I_Assert(W_CheckNumForName(G_BuildMapName(mapnum)) != LUMPERROR); buf_p = buf; if (pencoremode) flags |= 1; @@ -2526,7 +2525,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r // new gametype value WRITEUINT8(buf_p, newgametype); - WRITESTRINGN(buf_p, mapname, MAX_WADPATH); + WRITEINT16(buf_p, mapnum); } if (delay == 1) @@ -2969,11 +2968,11 @@ static void Command_Map_f(void) */ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) { - char mapname[MAX_WADPATH+1]; UINT8 flags; INT32 resetplayer = 1, lastgametype; UINT8 skipprecutscene, FLS; boolean pencoremode; + INT16 mapnumber; forceresetplayers = deferencoremode = false; @@ -3010,7 +3009,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) FLS = ((flags & (1<<3)) != 0); - READSTRINGN(*cp, mapname, MAX_WADPATH); + mapnumber = READINT16(*cp); if (netgame) P_SetRandSeed(READUINT32(*cp)); @@ -3018,7 +3017,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) if (!skipprecutscene) { DEBFILE(va("Warping to %s [resetplayer=%d lastgametype=%d gametype=%d cpnd=%d]\n", - mapname, resetplayer, lastgametype, gametype, chmappending)); + G_BuildMapName(mapnumber), resetplayer, lastgametype, gametype, chmappending)); CON_LogMessage(M_GetText("Speeding off to level...\n")); } @@ -3034,7 +3033,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? DSM_WILLAUTOSAVE : DSM_NOTSAVING; demo.savebutton = 0; - G_InitNew(pencoremode, mapname, resetplayer, skipprecutscene, FLS); + G_InitNew(pencoremode, mapnumber, resetplayer, skipprecutscene, FLS); if (demo.playback && !demo.timing) precache = true; if (demo.timing) @@ -5255,8 +5254,9 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) { INT32 i; UINT8 gt, secondgt; + INT16 tempvotelevels[4][2]; - if (playernum != serverplayer && !IsPlayerAdmin(playernum)) + if (playernum != serverplayer) // admin shouldn't be able to set up vote... { CONS_Alert(CONS_WARNING, M_GetText("Illegal vote setup received from %s\n"), player_names[playernum]); if (server) @@ -5276,10 +5276,15 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) for (i = 0; i < 4; i++) { - votelevels[i][0] = (UINT16)READUINT16(*cp); - votelevels[i][1] = gt; - if (!mapheaderinfo[votelevels[i][0]]) - P_AllocMapHeader(votelevels[i][0]); + tempvotelevels[i][0] = (UINT16)READUINT16(*cp); + tempvotelevels[i][1] = gt; + if (tempvotelevels[i][0] < nummapheaders && mapheaderinfo[tempvotelevels[i][0]]) + continue; + + if (server) + I_Error("Got_SetupVotecmd: Internal map ID %d not found (nummapheaders = %d)", tempvotelevels[i][0], nummapheaders); + CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad map ID %d received from %s\n"), tempvotelevels[i][0], player_names[playernum]); + return; } // If third entry has an illelegal Encore flag... (illelegal!?) @@ -5290,12 +5295,14 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) // Apply it to the second entry instead, gametype permitting! if (gametypedefaultrules[gt] & GTR_CIRCUIT) { - votelevels[1][1] |= VOTEMODIFIER_ENCORE; + tempvotelevels[1][1] |= VOTEMODIFIER_ENCORE; } } // Finally, set third entry's gametype/Encore status. - votelevels[2][1] = secondgt; + tempvotelevels[2][1] = secondgt; + + memcpy(votelevels, tempvotelevels, sizeof(votelevels)); G_SetGamestate(GS_VOTING); Y_StartVote(); diff --git a/src/deh_lua.c b/src/deh_lua.c index 2084872e8..9c407dbda 100644 --- a/src/deh_lua.c +++ b/src/deh_lua.c @@ -24,6 +24,7 @@ #include "dehacked.h" #include "deh_lua.h" #include "deh_tables.h" +#include "deh_soc.h" // freeslotusage // freeslot takes a name (string only!) // and allocates it to the appropriate free slot. @@ -474,18 +475,6 @@ static inline int lib_getenum(lua_State *L) } return luaL_error(L, "skincolor '%s' could not be found.\n", word); } - else if (fastncmp("GRADE_",word,6)) - { - p = word+6; - for (i = 0; NIGHTSGRADE_LIST[i]; i++) - if (*p == NIGHTSGRADE_LIST[i]) - { - lua_pushinteger(L, i); - return 1; - } - if (mathlib) return luaL_error(L, "NiGHTS grade '%s' could not be found.\n", word); - return 0; - } else if (fastncmp("PRECIP_",word,7)) { p = word+7; for (i = 0; i < MAXPRECIP; i++) diff --git a/src/deh_soc.c b/src/deh_soc.c index c1197c3cb..427493f28 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -140,28 +140,50 @@ void clear_conditionsets(void) void clear_levels(void) { - INT16 i; - // This is potentially dangerous but if we're resetting these headers, // we may as well try to save some memory, right? - for (i = 0; i < NUMMAPS; ++i) + while (nummapheaders > 0) { - if (!mapheaderinfo[i] || i == (tutorialmap-1)) + nummapheaders--; + + if (!mapheaderinfo[nummapheaders]) continue; // Custom map header info // (no need to set num to 0, we're freeing the entire header shortly) - Z_Free(mapheaderinfo[i]->customopts); + Z_Free(mapheaderinfo[nummapheaders]->customopts); - P_DeleteFlickies(i); - P_DeleteGrades(i); + P_DeleteFlickies(nummapheaders); - Z_Free(mapheaderinfo[i]); - mapheaderinfo[i] = NULL; + Z_Free(mapheaderinfo[nummapheaders]->mainrecord); + + Patch_Free(mapheaderinfo[nummapheaders]->thumbnailPic); + Patch_Free(mapheaderinfo[nummapheaders]->minimapPic); + + Z_Free(mapheaderinfo[nummapheaders]->lumpname); + + Z_Free(mapheaderinfo[nummapheaders]); + mapheaderinfo[nummapheaders] = NULL; } - // Realloc the one for the current gamemap as a safeguard - P_AllocMapHeader(gamemap-1); + // Clear out the cache + { + cupheader_t *cup = kartcupheaders; + UINT8 i; + + while (cup) + { + for (i = 0; i < CUPCACHE_MAX; i++) + { + cup->cachedlevels[i] = NEXTMAP_INVALID; + } + cup = cup->next; + } + } + + // Exit the current gamemap as a safeguard + if (Playing()) + COM_BufAddText("exitgame"); // Command_ExitGame_f() but delayed } // TODO: Figure out how to do undolines for this.... @@ -838,17 +860,33 @@ void readgametype(MYFILE *f, char *gtname) CONS_Printf("Added gametype %s\n", Gametype_Names[newgtidx]); } -void readlevelheader(MYFILE *f, INT32 num) +void readlevelheader(MYFILE *f, char * name) { char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; char *word2; //char *word3; // Non-uppercase version of word2 + char *tmp; INT32 i; - // Reset all previous map header information - P_AllocMapHeader((INT16)(num-1)); + INT32 num = G_MapNumber(name); + + if (num >= nummapheaders) + { + P_AllocMapHeader((INT16)(num = nummapheaders)); + } + else if (f->wad > mainwads) + { + // only mark as a major mod if it replaces an already-existing mapheaderinfo + G_SetGameModified(multiplayer, true); + } + + if (mapheaderinfo[num]->lumpname == NULL) + { + mapheaderinfo[num]->lumpname = Z_StrDup(name); + } do { @@ -886,16 +924,15 @@ void readlevelheader(MYFILE *f, INT32 num) if (fastcmp(word, "LEVELNAME")) { - deh_strlcpy(mapheaderinfo[num-1]->lvlttl, word2, - sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num)); - strlcpy(mapheaderinfo[num-1]->selectheading, word2, sizeof(mapheaderinfo[num-1]->selectheading)); // not deh_ so only complains once + deh_strlcpy(mapheaderinfo[num]->lvlttl, word2, + sizeof(mapheaderinfo[num]->lvlttl), va("Level header %d: levelname", num)); continue; } // CHEAP HACK: move this over here for lowercase subtitles if (fastcmp(word, "SUBTITLE")) { - deh_strlcpy(mapheaderinfo[num-1]->subttl, word2, - sizeof(mapheaderinfo[num-1]->subttl), va("Level header %d: subtitle", num)); + deh_strlcpy(mapheaderinfo[num]->subttl, word2, + sizeof(mapheaderinfo[num]->subttl), va("Level header %d: subtitle", num)); continue; } @@ -917,19 +954,19 @@ void readlevelheader(MYFILE *f, INT32 num) } // Sanity limit of 128 params - if (mapheaderinfo[num-1]->numCustomOptions == 128) + if (mapheaderinfo[num]->numCustomOptions == 128) { deh_warning("Level header %d: too many custom parameters", num); continue; } - j = mapheaderinfo[num-1]->numCustomOptions++; + j = mapheaderinfo[num]->numCustomOptions++; - mapheaderinfo[num-1]->customopts = - Z_Realloc(mapheaderinfo[num-1]->customopts, - sizeof(customoption_t) * mapheaderinfo[num-1]->numCustomOptions, PU_STATIC, NULL); + mapheaderinfo[num]->customopts = + Z_Realloc(mapheaderinfo[num]->customopts, + sizeof(customoption_t) * mapheaderinfo[num]->numCustomOptions, PU_STATIC, NULL); // Newly allocated - modoption = &mapheaderinfo[num-1]->customopts[j]; + modoption = &mapheaderinfo[num]->customopts[j]; strncpy(modoption->option, word, 31); modoption->option[31] = '\0'; @@ -945,33 +982,33 @@ void readlevelheader(MYFILE *f, INT32 num) if (fastcmp(word, "FLICKYLIST") || fastcmp(word, "ANIMALLIST")) { if (fastcmp(word2, "NONE")) - P_DeleteFlickies(num-1); + P_DeleteFlickies(num); else if (fastcmp(word2, "DEMO")) - P_SetDemoFlickies(num-1); + P_SetDemoFlickies(num); else if (fastcmp(word2, "ALL")) { mobjtype_t tmpflickies[MAXFLICKIES]; - for (mapheaderinfo[num-1]->numFlickies = 0; - ((mapheaderinfo[num-1]->numFlickies < MAXFLICKIES) && FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type); - mapheaderinfo[num-1]->numFlickies++) - tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type; + for (mapheaderinfo[num]->numFlickies = 0; + ((mapheaderinfo[num]->numFlickies < MAXFLICKIES) && FLICKYTYPES[mapheaderinfo[num]->numFlickies].type); + mapheaderinfo[num]->numFlickies++) + tmpflickies[mapheaderinfo[num]->numFlickies] = FLICKYTYPES[mapheaderinfo[num]->numFlickies].type; - if (mapheaderinfo[num-1]->numFlickies) // just in case... + if (mapheaderinfo[num]->numFlickies) // just in case... { - size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies; - mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL); - M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize); + size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num]->numFlickies; + mapheaderinfo[num]->flickies = Z_Realloc(mapheaderinfo[num]->flickies, newsize, PU_STATIC, NULL); + M_Memcpy(mapheaderinfo[num]->flickies, tmpflickies, newsize); } } else { mobjtype_t tmpflickies[MAXFLICKIES]; - mapheaderinfo[num-1]->numFlickies = 0; + mapheaderinfo[num]->numFlickies = 0; tmp = strtok(word2,","); // get up to the first MAXFLICKIES flickies do { - if (mapheaderinfo[num-1]->numFlickies == MAXFLICKIES) // never going to get above that number + if (mapheaderinfo[num]->numFlickies == MAXFLICKIES) // never going to get above that number { deh_warning("Level header %d: too many flickies\n", num); break; @@ -985,7 +1022,7 @@ void readlevelheader(MYFILE *f, INT32 num) //deh_warning("Level header %d: unknown flicky mobj type %s\n", num, tmp); -- no need for this line as get_mobjtype complains too continue; } - tmpflickies[mapheaderinfo[num-1]->numFlickies] = i; + tmpflickies[mapheaderinfo[num]->numFlickies] = i; } else // ...or a quick, limited selection of default flickies! { @@ -998,17 +1035,17 @@ void readlevelheader(MYFILE *f, INT32 num) deh_warning("Level header %d: unknown flicky selection %s\n", num, tmp); continue; } - tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[i].type; + tmpflickies[mapheaderinfo[num]->numFlickies] = FLICKYTYPES[i].type; } - mapheaderinfo[num-1]->numFlickies++; + mapheaderinfo[num]->numFlickies++; } while ((tmp = strtok(NULL,",")) != NULL); - if (mapheaderinfo[num-1]->numFlickies) + if (mapheaderinfo[num]->numFlickies) { - size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies; - mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL); + size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num]->numFlickies; + mapheaderinfo[num]->flickies = Z_Realloc(mapheaderinfo[num]->flickies, newsize, PU_STATIC, NULL); // now we add them to the list! - M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize); + M_Memcpy(mapheaderinfo[num]->flickies, tmpflickies, newsize); } else deh_warning("Level header %d: no valid flicky types found\n", num); @@ -1018,62 +1055,30 @@ void readlevelheader(MYFILE *f, INT32 num) // Strings that can be truncated else if (fastcmp(word, "ZONETITLE")) { - deh_strlcpy(mapheaderinfo[num-1]->zonttl, word2, - sizeof(mapheaderinfo[num-1]->zonttl), va("Level header %d: zonetitle", num)); + deh_strlcpy(mapheaderinfo[num]->zonttl, word2, + sizeof(mapheaderinfo[num]->zonttl), va("Level header %d: zonetitle", num)); } else if (fastcmp(word, "SCRIPTNAME")) { - deh_strlcpy(mapheaderinfo[num-1]->scriptname, word2, - sizeof(mapheaderinfo[num-1]->scriptname), va("Level header %d: scriptname", num)); + deh_strlcpy(mapheaderinfo[num]->scriptname, word2, + sizeof(mapheaderinfo[num]->scriptname), va("Level header %d: scriptname", num)); } else if (fastcmp(word, "RUNSOC")) { - deh_strlcpy(mapheaderinfo[num-1]->runsoc, word2, - sizeof(mapheaderinfo[num-1]->runsoc), va("Level header %d: runsoc", num)); + deh_strlcpy(mapheaderinfo[num]->runsoc, word2, + sizeof(mapheaderinfo[num]->runsoc), va("Level header %d: runsoc", num)); } else if (fastcmp(word, "ACT")) { if (i >= 0 && i <= 99) // 0 for no act number - mapheaderinfo[num-1]->actnum = (UINT8)i; + mapheaderinfo[num]->actnum = (UINT8)i; else deh_warning("Level header %d: invalid act number %d", num, i); } - else if (fastcmp(word, "NEXTLEVEL")) - { - if (fastcmp(word2, "TITLE")) i = 1100; - else if (fastcmp(word2, "EVALUATION")) i = 1101; - else if (fastcmp(word2, "CREDITS")) i = 1102; - else if (fastcmp(word2, "ENDING")) i = 1103; - else - // Support using the actual map name, - // i.e., Nextlevel = AB, Nextlevel = FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') - i = M_MapNumber(word2[0], word2[1]); - - mapheaderinfo[num-1]->nextlevel = (INT16)i; - } - else if (fastcmp(word, "MARATHONNEXT")) - { - if (fastcmp(word2, "TITLE")) i = 1100; - else if (fastcmp(word2, "EVALUATION")) i = 1101; - else if (fastcmp(word2, "CREDITS")) i = 1102; - else if (fastcmp(word2, "ENDING")) i = 1103; - else - // Support using the actual map name, - // i.e., MarathonNext = AB, MarathonNext = FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') - i = M_MapNumber(word2[0], word2[1]); - - mapheaderinfo[num-1]->marathonnext = (INT16)i; - } else if (fastcmp(word, "TYPEOFLEVEL")) { if (i) // it's just a number - mapheaderinfo[num-1]->typeoflevel = (UINT32)i; + mapheaderinfo[num]->typeoflevel = (UINT32)i; else { UINT32 tol = 0; @@ -1086,152 +1091,147 @@ void readlevelheader(MYFILE *f, INT32 num) deh_warning("Level header %d: unknown typeoflevel flag %s\n", num, tmp); tol |= TYPEOFLEVEL[i].flag; } while((tmp = strtok(NULL,",")) != NULL); - mapheaderinfo[num-1]->typeoflevel = tol; + mapheaderinfo[num]->typeoflevel = tol; } } else if (fastcmp(word, "KEYWORDS")) { - deh_strlcpy(mapheaderinfo[num-1]->keywords, word2, - sizeof(mapheaderinfo[num-1]->keywords), va("Level header %d: keywords", num)); + deh_strlcpy(mapheaderinfo[num]->keywords, word2, + sizeof(mapheaderinfo[num]->keywords), va("Level header %d: keywords", num)); } else if (fastcmp(word, "MUSIC")) { if (fastcmp(word2, "NONE")) - mapheaderinfo[num-1]->musname[0] = 0; // becomes empty string + mapheaderinfo[num]->musname[0] = 0; // becomes empty string else { - deh_strlcpy(mapheaderinfo[num-1]->musname, word2, - sizeof(mapheaderinfo[num-1]->musname), va("Level header %d: music", num)); + deh_strlcpy(mapheaderinfo[num]->musname, word2, + sizeof(mapheaderinfo[num]->musname), va("Level header %d: music", num)); } } else if (fastcmp(word, "MUSICSLOT")) deh_warning("Level header %d: MusicSlot parameter is deprecated and will be removed.\nUse \"Music\" instead.", num); else if (fastcmp(word, "MUSICTRACK")) - mapheaderinfo[num-1]->mustrack = ((UINT16)i - 1); + mapheaderinfo[num]->mustrack = ((UINT16)i - 1); else if (fastcmp(word, "MUSICPOS")) - mapheaderinfo[num-1]->muspos = (UINT32)get_number(word2); - else if (fastcmp(word, "FORCECHARACTER")) - { - strlcpy(mapheaderinfo[num-1]->forcecharacter, word2, SKINNAMESIZE+1); - strlwr(mapheaderinfo[num-1]->forcecharacter); // skin names are lowercase - } + mapheaderinfo[num]->muspos = (UINT32)get_number(word2); else if (fastcmp(word, "WEATHER")) - mapheaderinfo[num-1]->weather = get_precip(word2); + mapheaderinfo[num]->weather = get_precip(word2); else if (fastcmp(word, "SKYTEXTURE")) - deh_strlcpy(mapheaderinfo[num-1]->skytexture, word2, - sizeof(mapheaderinfo[num-1]->skytexture), va("Level header %d: sky texture", num)); + deh_strlcpy(mapheaderinfo[num]->skytexture, word2, + sizeof(mapheaderinfo[num]->skytexture), va("Level header %d: sky texture", num)); else if (fastcmp(word, "PRECUTSCENENUM")) - mapheaderinfo[num-1]->precutscenenum = (UINT8)i; + mapheaderinfo[num]->precutscenenum = (UINT8)i; else if (fastcmp(word, "CUTSCENENUM")) - mapheaderinfo[num-1]->cutscenenum = (UINT8)i; + mapheaderinfo[num]->cutscenenum = (UINT8)i; else if (fastcmp(word, "PALETTE")) - mapheaderinfo[num-1]->palette = (UINT16)i; + mapheaderinfo[num]->palette = (UINT16)i; else if (fastcmp(word, "ENCOREPAL")) - mapheaderinfo[num-1]->encorepal = (UINT16)i; + mapheaderinfo[num]->encorepal = (UINT16)i; else if (fastcmp(word, "NUMLAPS")) - mapheaderinfo[num-1]->numlaps = (UINT8)i; + mapheaderinfo[num]->numlaps = (UINT8)i; else if (fastcmp(word, "UNLOCKABLE")) { if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something - mapheaderinfo[num-1]->unlockrequired = (SINT8)i - 1; + mapheaderinfo[num]->unlockrequired = (SINT8)i - 1; else deh_warning("Level header %d: invalid unlockable number %d", num, i); } else if (fastcmp(word, "SKYBOXSCALE")) - mapheaderinfo[num-1]->skybox_scalex = mapheaderinfo[num-1]->skybox_scaley = mapheaderinfo[num-1]->skybox_scalez = (INT16)i; + mapheaderinfo[num]->skybox_scalex = mapheaderinfo[num]->skybox_scaley = mapheaderinfo[num]->skybox_scalez = (INT16)i; else if (fastcmp(word, "SKYBOXSCALEX")) - mapheaderinfo[num-1]->skybox_scalex = (INT16)i; + mapheaderinfo[num]->skybox_scalex = (INT16)i; else if (fastcmp(word, "SKYBOXSCALEY")) - mapheaderinfo[num-1]->skybox_scaley = (INT16)i; + mapheaderinfo[num]->skybox_scaley = (INT16)i; else if (fastcmp(word, "SKYBOXSCALEZ")) - mapheaderinfo[num-1]->skybox_scalez = (INT16)i; + mapheaderinfo[num]->skybox_scalez = (INT16)i; else if (fastcmp(word, "LEVELFLAGS")) - mapheaderinfo[num-1]->levelflags = get_number(word2); + mapheaderinfo[num]->levelflags = get_number(word2); else if (fastcmp(word, "MENUFLAGS")) - mapheaderinfo[num-1]->menuflags = get_number(word2); + mapheaderinfo[num]->menuflags = get_number(word2); // SRB2Kart else if (fastcmp(word, "MOBJSCALE")) - mapheaderinfo[num-1]->mobj_scale = get_number(word2); + mapheaderinfo[num]->mobj_scale = get_number(word2); else if (fastcmp(word, "DEFAULTWAYPOINTRADIUS")) - mapheaderinfo[num-1]->default_waypoint_radius = get_number(word2); + mapheaderinfo[num]->default_waypoint_radius = get_number(word2); else if (fastcmp(word, "LIGHTCONTRAST")) { - mapheaderinfo[num-1]->light_contrast = (UINT8)i; + mapheaderinfo[num]->light_contrast = (UINT8)i; } else if (fastcmp(word, "LIGHTANGLE")) { if (fastcmp(word2, "EVEN")) { - mapheaderinfo[num-1]->use_light_angle = false; - mapheaderinfo[num-1]->light_angle = 0; + mapheaderinfo[num]->use_light_angle = false; + mapheaderinfo[num]->light_angle = 0; } else { - mapheaderinfo[num-1]->use_light_angle = true; - mapheaderinfo[num-1]->light_angle = FixedAngle(FloatToFixed(atof(word2))); + mapheaderinfo[num]->use_light_angle = true; + mapheaderinfo[num]->light_angle = FixedAngle(FloatToFixed(atof(word2))); } } // Individual triggers for level flags, for ease of use (and 2.0 compatibility) else if (fastcmp(word, "SCRIPTISFILE")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_SCRIPTISFILE; + mapheaderinfo[num]->levelflags |= LF_SCRIPTISFILE; else - mapheaderinfo[num-1]->levelflags &= ~LF_SCRIPTISFILE; + mapheaderinfo[num]->levelflags &= ~LF_SCRIPTISFILE; } else if (fastcmp(word, "NOZONE")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_NOZONE; + mapheaderinfo[num]->levelflags |= LF_NOZONE; else - mapheaderinfo[num-1]->levelflags &= ~LF_NOZONE; + mapheaderinfo[num]->levelflags &= ~LF_NOZONE; } else if (fastcmp(word, "SECTIONRACE")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_SECTIONRACE; + mapheaderinfo[num]->levelflags |= LF_SECTIONRACE; else - mapheaderinfo[num-1]->levelflags &= ~LF_SECTIONRACE; + mapheaderinfo[num]->levelflags &= ~LF_SECTIONRACE; } else if (fastcmp(word, "SUBTRACTNUM")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_SUBTRACTNUM; + mapheaderinfo[num]->levelflags |= LF_SUBTRACTNUM; else - mapheaderinfo[num-1]->levelflags &= ~LF_SUBTRACTNUM; + mapheaderinfo[num]->levelflags &= ~LF_SUBTRACTNUM; } // Individual triggers for menu flags else if (fastcmp(word, "HIDDEN")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_HIDEINMENU; + mapheaderinfo[num]->menuflags |= LF2_HIDEINMENU; else - mapheaderinfo[num-1]->menuflags &= ~LF2_HIDEINMENU; + mapheaderinfo[num]->menuflags &= ~LF2_HIDEINMENU; } else if (fastcmp(word, "HIDEINSTATS")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_HIDEINSTATS; + mapheaderinfo[num]->menuflags |= LF2_HIDEINSTATS; else - mapheaderinfo[num-1]->menuflags &= ~LF2_HIDEINSTATS; + mapheaderinfo[num]->menuflags &= ~LF2_HIDEINSTATS; } - else if (fastcmp(word, "TIMEATTACK") || fastcmp(word, "RECORDATTACK")) + else if (fastcmp(word, "NOTIMEATTACK") || fastcmp(word, "NORECORDATTACK")) { // RECORDATTACK is an accepted alias if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags &= ~LF2_NOTIMEATTACK; + mapheaderinfo[num]->menuflags |= LF2_NOTIMEATTACK; else - mapheaderinfo[num-1]->menuflags |= LF2_NOTIMEATTACK; + mapheaderinfo[num]->menuflags &= ~LF2_NOTIMEATTACK; } else if (fastcmp(word, "VISITNEEDED")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_VISITNEEDED; + mapheaderinfo[num]->menuflags |= LF2_VISITNEEDED; else - mapheaderinfo[num-1]->menuflags &= ~LF2_VISITNEEDED; + mapheaderinfo[num]->menuflags &= ~LF2_VISITNEEDED; } else if (fastcmp(word, "GRAVITY")) - mapheaderinfo[num-1]->gravity = FLOAT_TO_FIXED(atof(word2)); + mapheaderinfo[num]->gravity = FLOAT_TO_FIXED(atof(word2)); else deh_warning("Level header %d: unknown word '%s'", num, word); } @@ -2103,16 +2103,9 @@ void reademblemdata(MYFILE *f, INT32 num) } else if (fastcmp(word, "TAG")) emblemlocations[num-1].tag = (INT16)value; - else if (fastcmp(word, "MAPNUM")) + else if (fastcmp(word, "MAPNAME")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - - emblemlocations[num-1].level = (INT16)value; + emblemlocations[num-1].level = Z_StrDup(word2); } else if (fastcmp(word, "SPRITE")) { @@ -2333,13 +2326,7 @@ void readunlockable(MYFILE *f, INT32 num) } else if (fastcmp(word, "VAR")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - i = M_MapNumber(word2[0], word2[1]); - + // TODO: different field for level name string unlockables[num].variable = (INT16)i; } else @@ -2378,7 +2365,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (!params[0]) { - deh_warning("condition line is empty"); + deh_warning("condition line is empty for condition ID %d", id); return; } @@ -2398,7 +2385,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (x1 < 0 || x1 >= PWRLV_NUMTYPES) { - deh_warning("Power level type %d out of range (0 - %d)", x1, PWRLV_NUMTYPES-1); + deh_warning("Power level type %d out of range (0 - %d) for condition ID %d", x1, PWRLV_NUMTYPES-1, id); return; } } @@ -2418,16 +2405,11 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) { PARAMCHECK(1); ty = UC_MAPVISITED + offset; + re = G_MapNumber(params[1]); - // Convert to map number if it appears to be one - if (params[1][0] >= 'A' && params[1][0] <= 'Z') - re = M_MapNumber(params[1][0], params[1][1]); - else - re = atoi(params[1]); - - if (re < 0 || re >= NUMMAPS) + if (re >= nummapheaders) { - deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS); + deh_warning("Invalid level %s for condition ID %d", params[1], id); return; } } @@ -2436,16 +2418,11 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) PARAMCHECK(2); ty = UC_MAPTIME; re = atoi(params[2]); + x1 = G_MapNumber(params[1]); - // Convert to map number if it appears to be one - if (params[1][0] >= 'A' && params[1][0] <= 'Z') - x1 = (INT16)M_MapNumber(params[1][0], params[1][1]); - else - x1 = (INT16)atoi(params[1]); - - if (x1 < 0 || x1 >= NUMMAPS) + if (x1 >= nummapheaders) { - deh_warning("Level number %d out of range (1 - %d)", x1, NUMMAPS); + deh_warning("Invalid level %s for condition ID %d", params[1], id); return; } } @@ -2458,7 +2435,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) // constrained by 32 bits if (re < 0 || re > 31) { - deh_warning("Trigger ID %d out of range (0 - 31)", re); + deh_warning("Trigger ID %d out of range (0 - 31) for condition ID %d", re, id); return; } } @@ -2476,7 +2453,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (re <= 0 || re > MAXEMBLEMS) { - deh_warning("Emblem %d out of range (1 - %d)", re, MAXEMBLEMS); + deh_warning("Emblem %d out of range (1 - %d) for condition ID %d", re, MAXEMBLEMS, id); return; } } @@ -2488,7 +2465,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (re <= 0 || re > MAXEXTRAEMBLEMS) { - deh_warning("Extra emblem %d out of range (1 - %d)", re, MAXEXTRAEMBLEMS); + deh_warning("Extra emblem %d out of range (1 - %d) for condition ID %d", re, MAXEXTRAEMBLEMS, id); return; } } @@ -2500,13 +2477,13 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (re <= 0 || re > MAXCONDITIONSETS) { - deh_warning("Condition set %d out of range (1 - %d)", re, MAXCONDITIONSETS); + deh_warning("Condition set %d out of range (1 - %d) for condition ID %d", re, MAXCONDITIONSETS, id); return; } } else { - deh_warning("Invalid condition name %s", params[0]); + deh_warning("Invalid condition name %s for condition ID %d", params[0], id); return; } @@ -2641,61 +2618,6 @@ void readmaincfg(MYFILE *f) COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE)); } } - - else if (fastcmp(word, "SPSTAGE_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - spstage_start = spmarathon_start = (INT16)value; - } - else if (fastcmp(word, "SPMARATHON_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - spmarathon_start = (INT16)value; - } - else if (fastcmp(word, "SSTAGE_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - sstage_start = (INT16)value; - sstage_end = (INT16)(sstage_start+7); // 7 special stages total plus one weirdo - } - else if (fastcmp(word, "SMPSTAGE_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - smpstage_start = (INT16)value; - smpstage_end = (INT16)(smpstage_start+6); // 7 special stages total - } else if (fastcmp(word, "REDTEAM")) { skincolor_redteam = (UINT16)get_number(word2); @@ -2778,16 +2700,7 @@ void readmaincfg(MYFILE *f) } else if (fastcmp(word, "TITLEMAP")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - titlemap = (INT16)value; + titlemap = Z_StrDup(word2); titlechanged = true; } else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "TITLEPICSHIDE")) @@ -2915,30 +2828,12 @@ void readmaincfg(MYFILE *f) } else if (fastcmp(word, "BOOTMAP")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - bootmap = (INT16)value; + bootmap = Z_StrDup(word2); //titlechanged = true; } else if (fastcmp(word, "TUTORIALMAP")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - tutorialmap = (INT16)value; + tutorialmap = Z_StrDup(word2); } else deh_warning("Maincfg: unknown word '%s'", word); @@ -3161,37 +3056,24 @@ void readcupheader(MYFILE *f, cupheader_t *cup) tmp = strtok(word2,","); do { - INT32 map = atoi(tmp); - - if (tmp[0] >= 'A' && tmp[0] <= 'Z' && tmp[2] == '\0') - map = M_MapNumber(tmp[0], tmp[1]); - - if (!map) - break; - if (cup->numlevels >= MAXLEVELLIST) { deh_warning("%s Cup: reached max levellist (%d)\n", cup->name, MAXLEVELLIST); break; } - cup->levellist[cup->numlevels] = map - 1; + cup->levellist[cup->numlevels] = Z_StrDup(tmp); + cup->cachedlevels[cup->numlevels] = NEXTMAP_INVALID; cup->numlevels++; } while((tmp = strtok(NULL,",")) != NULL); } else if (fastcmp(word, "BONUSGAME")) { - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') - i = M_MapNumber(word2[0], word2[1]); - cup->bonusgame = (INT16)i - 1; + cup->levellist[CUPCACHE_BONUS] = Z_StrDup(word2); } else if (fastcmp(word, "SPECIALSTAGE")) { - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') - i = M_MapNumber(word2[0], word2[1]); - cup->specialstage = (INT16)i - 1; + cup->levellist[CUPCACHE_SPECIAL] = Z_StrDup(word2); } else if (fastcmp(word, "EMERALDNUM")) { @@ -3902,19 +3784,6 @@ static fixed_t find_const(const char **rword) free(word); return 0; } - else if (fastncmp("GRADE_",word,6)) - { - char *p = word+6; - for (i = 0; NIGHTSGRADE_LIST[i]; i++) - if (*p == NIGHTSGRADE_LIST[i]) - { - free(word); - return i; - } - const_warning("NiGHTS grade",word); - free(word); - return 0; - } for (i = 0; INT_CONST[i].n; i++) if (fastcmp(word,INT_CONST[i].n)) { free(word); diff --git a/src/deh_soc.h b/src/deh_soc.h index 335260953..161422783 100644 --- a/src/deh_soc.h +++ b/src/deh_soc.h @@ -68,7 +68,7 @@ void readhuditem(MYFILE *f, INT32 num); void readmenu(MYFILE *f, INT32 num); void readtextprompt(MYFILE *f, INT32 num); void readcutscene(MYFILE *f, INT32 num); -void readlevelheader(MYFILE *f, INT32 num); +void readlevelheader(MYFILE *f, char * name); void readgametype(MYFILE *f, char *gtname); void readsprite2(MYFILE *f, INT32 num); #ifdef HWRENDER diff --git a/src/deh_tables.c b/src/deh_tables.c index a22b2f50a..b25bf7259 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -32,17 +32,6 @@ char *FREE_MOBJS[NUMMOBJFREESLOTS]; char *FREE_SKINCOLORS[NUMCOLORFREESLOTS]; UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite freeslot in use! I would use ceil() here if I could, but it only saves 1 byte of memory anyway. -const char NIGHTSGRADE_LIST[] = { - 'F', // GRADE_F - 'E', // GRADE_E - 'D', // GRADE_D - 'C', // GRADE_C - 'B', // GRADE_B - 'A', // GRADE_A - 'S', // GRADE_S - '\0' -}; - struct flickytypes_s FLICKYTYPES[] = { {"BLUEBIRD", MT_FLICKY_01}, // Flicky (Flicky) {"RABBIT", MT_FLICKY_02}, // Pocky (1) diff --git a/src/deh_tables.h b/src/deh_tables.h index 294e6452a..05fb38d67 100644 --- a/src/deh_tables.h +++ b/src/deh_tables.h @@ -53,7 +53,6 @@ struct int_const_s { lua_Integer v; }; -extern const char NIGHTSGRADE_LIST[]; extern struct flickytypes_s FLICKYTYPES[]; extern actionpointer_t actionpointers[]; // Array mapping action names to action functions. extern const char *const STATE_LIST[]; diff --git a/src/dehacked.c b/src/dehacked.c index 4187a522b..690a49dbc 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -358,25 +358,14 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) #endif else if (fastcmp(word, "LEVEL")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - i = M_MapNumber(word2[0], word2[1]); - - if (i > 0 && i <= NUMMAPS) + size_t len = strlen(word2); + if (len <= MAXMAPLUMPNAME-1) { - if (mapheaderinfo[i]) - { - G_SetGameModified(multiplayer, true); // Only a major mod if editing stuff that isn't your own! - } - - readlevelheader(f, i); + readlevelheader(f, word2); } else { - deh_warning("Level number %d out of range (1 - %d)", i, NUMMAPS); + deh_warning("Map header's lumpname %s is too long (%s characters VS %d max)", word2, sizeu1(len), (MAXMAPLUMPNAME-1)); ignorelines(f); } } diff --git a/src/discord.c b/src/discord.c index e178dbe2c..0e9cb35c9 100644 --- a/src/discord.c +++ b/src/discord.c @@ -405,7 +405,9 @@ void DRPC_UpdatePresence(void) { char detailstr[48+1]; +#ifdef USEMAPIMG char mapimg[8+1]; +#endif char mapname[5+21+21+2+1]; char charimg[4+SKINNAMESIZE+1]; @@ -508,14 +510,18 @@ void DRPC_UpdatePresence(void) if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) // Map info && !(demo.playback && demo.title)) { +#ifdef USEMAPIMG if ((gamemap >= 1 && gamemap <= 60) // supported race maps || (gamemap >= 136 && gamemap <= 164)) // supported battle maps { - snprintf(mapimg, 8, "%s", G_BuildMapName(gamemap)); + //FIXME + //snprintf(mapimg, 8, "%s", G_BuildMapName(gamemap)); strlwr(mapimg); discordPresence.largeImageKey = mapimg; // Map image } - else if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU) + else +#endif + if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU) { // Hell map, use the method that got you here :P discordPresence.largeImageKey = "miscdice"; diff --git a/src/doomdata.h b/src/doomdata.h index b891bc8a4..dbe7c1f84 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -218,8 +218,6 @@ typedef struct #define ZSHIFT 4 -#define NUMMAPS 1035 - /* slope thing types */ enum { diff --git a/src/doomstat.h b/src/doomstat.h index 30578d3e6..9758172e8 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -25,6 +25,9 @@ // We need the player data structure as well. #include "d_player.h" +// For lumpnum_t. +#include "w_wad.h" + // ============================= // Selected map etc. // ============================= @@ -102,6 +105,23 @@ extern preciptype_t precip_freeslot; extern preciptype_t globalweather; extern preciptype_t curWeather; +/** Time attack information, currently a very small structure. + */ +typedef struct +{ + tic_t time; ///< Time in which the level was finished. + tic_t lap; ///< Best lap time for this level. + //UINT32 score; ///< Score when the level was finished. + //UINT16 rings; ///< Rings when the level was finished. +} recorddata_t; + +// mapvisited is now a set of flags that says what we've done in the map. +#define MV_VISITED (1) +#define MV_BEATEN (1<<1) +#define MV_ENCORE (1<<2) +#define MV_MAX (MV_VISITED|MV_BEATEN|MV_ENCORE) +#define MV_MP ((MV_MAX+1)<<1) + // Set if homebrew PWAD stuff has been added. extern boolean modifiedgame; extern boolean majormods; @@ -185,15 +205,11 @@ extern INT32 splitscreen_party[MAXPLAYERS][MAXSPLITSCREENPLAYERS]; /* the only local one */ extern boolean splitscreen_partied[MAXPLAYERS]; -// Maps of special importance -extern INT16 spstage_start, spmarathon_start; -extern INT16 sstage_start, sstage_end, smpstage_start, smpstage_end; - -extern INT16 titlemap; +extern char * titlemap; extern boolean hidetitlepics; -extern INT16 bootmap; //bootmap for loading a map on startup +extern char * bootmap; //bootmap for loading a map on startup -extern INT16 tutorialmap; // map to load for tutorial +extern char * tutorialmap; // map to load for tutorial extern boolean tutorialmode; // are we in a tutorial right now? extern boolean looptitle; @@ -201,8 +217,6 @@ extern boolean looptitle; // CTF colors. extern UINT16 skincolor_redteam, skincolor_blueteam, skincolor_redring, skincolor_bluering; -extern tic_t countdowntimer; -extern boolean countdowntimeup; extern boolean exitfadestarted; typedef struct @@ -293,8 +307,6 @@ extern textprompt_t *textprompts[MAX_PROMPTS]; extern INT16 nextmapoverride; extern UINT8 skipstats; -extern UINT32 ssspheres; // Total # of spheres in a level - // Fun extra stuff extern INT16 lastmap; // Last level you were at (returning from special stages). extern mobj_t *redflag, *blueflag; // Pointers to physical flags @@ -319,106 +331,113 @@ extern struct quake fixed_t radius, intensity; } quake; -// NiGHTS grades -typedef struct -{ - UINT32 grade[6]; // D, C, B, A, S, X (F: failed to reach any of these) -} nightsgrades_t; - // Custom Lua values -// (This is not ifdeffed so the map header structure can stay identical, just in case.) typedef struct { char option[32]; // 31 usable characters char value[256]; // 255 usable characters. If this seriously isn't enough then wtf. } customoption_t; +// This could support more, but is that a good idea? +// Keep in mind that it may encourage people making overly long cups just because they "can", and would be a waste of memory. +#define MAXLEVELLIST 5 +#define CUPCACHE_BONUS MAXLEVELLIST +#define CUPCACHE_SPECIAL MAXLEVELLIST+1 +#define CUPCACHE_MAX CUPCACHE_SPECIAL+1 + +typedef struct cupheader_s +{ + UINT16 id; ///< Cup ID + char name[15]; ///< Cup title (14 chars) + char icon[9]; ///< Name of the icon patch + char *levellist[CUPCACHE_MAX]; ///< List of levels that belong to this cup + INT16 cachedlevels[CUPCACHE_MAX]; ///< IDs in levellist, bonusgame, and specialstage + UINT8 numlevels; ///< Number of levels defined in levellist + UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald) + SINT8 unlockrequired; ///< An unlockable is required to select this cup. -1 for no unlocking required. + struct cupheader_s *next; ///< Next cup in linked list +} cupheader_t; + +extern cupheader_t *kartcupheaders; // Start of cup linked list +extern UINT16 numkartcupheaders; + +#define MAXMAPLUMPNAME 64 // includes \0, for cleaner savedata + /** Map header information. */ typedef struct { - // The original eight, plus one. - char lvlttl[22]; ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway) - char subttl[33]; ///< Subtitle for level - char zonttl[22]; ///< "ZONE" replacement name - UINT8 actnum; ///< Act number or 0 for none. - UINT32 typeoflevel; ///< Combination of typeoflevel flags. - INT16 nextlevel; ///< Map number of next level, or 1100-1102 to end. - INT16 marathonnext; ///< See nextlevel, but for Marathon mode. Necessary to support hub worlds ala SUGOI. - char keywords[33]; ///< Keywords separated by space to search for. 32 characters. - char musname[7]; ///< Music track to play. "" for no music. - UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore. - UINT32 muspos; ///< Music position to jump to. - char forcecharacter[17]; ///< (SKINNAMESIZE+1) Skin to switch to or "" to disable. - UINT8 weather; ///< 0 = sunny day, 1 = storm, 2 = snow, 3 = rain, 4 = blank, 5 = thunder w/o rain, 6 = rain w/o lightning, 7 = heat wave. - char skytexture[9]; ///< Sky texture to use. - INT16 skybox_scalex; ///< Skybox X axis scale. (0 = no movement, 1 = 1:1 movement, 16 = 16:1 slow movement, -4 = 1:4 fast movement, etc.) - INT16 skybox_scaley; ///< Skybox Y axis scale. - INT16 skybox_scalez; ///< Skybox Z axis scale. + // Core game information, not user-modifiable directly + char *lumpname; ///< Lump name can be really long + lumpnum_t lumpnum; ///< Lump number for the map, used by vres_GetMap - // Extra information. - char interscreen[8]; ///< 320x200 patch to display at intermission. - char runsoc[33]; ///< SOC to execute at start of level (32 character limit instead of 63) - char scriptname[33]; ///< Script to use when the map is switched to. (32 character limit instead of 191) - UINT8 precutscenenum; ///< Cutscene number to play BEFORE a level starts. - UINT8 cutscenenum; ///< Cutscene number to use, 0 for none. - INT16 countdown; ///< Countdown until level end? - UINT16 palette; ///< PAL lump to use on this map - UINT16 encorepal; ///< PAL for encore mode - UINT8 numlaps; ///< Number of laps in circuit mode, unless overridden. - SINT8 unlockrequired; ///< Is an unlockable required to play this level? -1 if no. - UINT8 levelselect; ///< Is this map available in the level select? If so, which map list is it available in? - SINT8 bonustype; ///< What type of bonus does this level have? (-1 for null.) - SINT8 maxbonuslives; ///< How many bonus lives to award at Intermission? (-1 for unlimited.) + void *thumbnailPic; ///< Lump data for the level select thumbnail. + void *minimapPic; ///< Lump data for the minimap graphic. + void *encoreLump; ///< Lump data for the Encore Mode remap. + void *tweakLump; ///< Lump data for the palette tweak remap. - UINT16 levelflags; ///< LF_flags: merged booleans into one UINT16 for space, see below - UINT8 menuflags; ///< LF2_flags: options that affect record attack / nights mode menus + UINT8 mapvisited; ///< A set of flags that says what we've done in the map. + recorddata_t *mainrecord; ///< Stores best time attack data - char selectheading[22]; ///< Level select heading. Allows for controllable grouping. - UINT16 startrings; ///< Number of rings players start with. - INT32 sstimer; ///< Timer for special stages. - UINT32 ssspheres; ///< Sphere requirement in special stages. - fixed_t gravity; ///< Map-wide gravity. + cupheader_t *cup; ///< Cached cup - // Title card. - char ltzzpatch[8]; ///< Zig zag patch. - char ltzztext[8]; ///< Zig zag text. - char ltactdiamond[8]; ///< Act diamond. + // Titlecard information + char lvlttl[22]; ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway) + char subttl[33]; ///< Subtitle for level + char zonttl[22]; ///< "ZONE" replacement name + UINT8 actnum; ///< Act number or 0 for none. - // Freed animals stuff. - UINT8 numFlickies; ///< Internal. For freed flicky support. - mobjtype_t *flickies; ///< List of freeable flickies in this level. Allocated dynamically for space reasons. Be careful. + // Selection metadata + char keywords[33]; ///< Keywords separated by space to search for. 32 characters. - // NiGHTS stuff. - UINT8 numGradedMares; ///< Internal. For grade support. - nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful. + SINT8 unlockrequired; ///< Is an unlockable required to play this level? -1 if no. + UINT8 levelselect; ///< Is this map available in the level select? If so, which map list is it available in? + UINT16 menuflags; ///< LF2_flags: options that affect record attack menus - // SRB2kart - fixed_t mobj_scale; ///< Replacement for TOL_ERZ3 - fixed_t default_waypoint_radius; ///< 0 is a special value for DEFAULT_WAYPOINT_RADIUS, but scaled with mobjscale + // Operational metadata + UINT16 levelflags; ///< LF_flags: merged booleans into one UINT16 for space, see below + UINT32 typeoflevel; ///< Combination of typeoflevel flags. + UINT8 numlaps; ///< Number of laps in circuit mode, unless overridden. + fixed_t gravity; ///< Map-wide gravity. - UINT8 light_contrast; ///< Range of wall lighting. 0 is no lighting. - boolean use_light_angle; ///< When false, wall lighting is evenly distributed. When true, wall lighting is directional. - angle_t light_angle; ///< Angle of directional wall lighting. + // Music information + char musname[7]; ///< Music track to play. "" for no music. + UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore. + UINT32 muspos; ///< Music position to jump to. - // Music stuff. - UINT32 musinterfadeout; ///< Fade out level music on intermission screen in milliseconds - char musintername[7]; ///< Intermission screen music. + // Sky information + UINT8 weather; ///< See preciptype_t + char skytexture[9]; ///< Sky texture to use. + INT16 skybox_scalex; ///< Skybox X axis scale. (0 = no movement, 1 = 1:1 movement, 16 = 16:1 slow movement, -4 = 1:4 fast movement, etc.) + INT16 skybox_scaley; ///< Skybox Y axis scale. + INT16 skybox_scalez; ///< Skybox Z axis scale. - char muspostbossname[7]; ///< Post-bossdeath music. - UINT16 muspostbosstrack; ///< Post-bossdeath track. - UINT32 muspostbosspos; ///< Post-bossdeath position - UINT32 muspostbossfadein; ///< Post-bossdeath fade-in milliseconds. + // Distance information + fixed_t mobj_scale; ///< Defines the size all object calculations are relative to + fixed_t default_waypoint_radius; ///< 0 is a special value for DEFAULT_WAYPOINT_RADIUS, but scaled with mobjscale - SINT8 musforcereset; ///< Force resetmusic (-1 for default; 0 for force off; 1 for force on) + // Visual information + UINT16 palette; ///< PAL lump to use on this map + UINT16 encorepal; ///< PAL for encore mode + UINT8 light_contrast; ///< Range of wall lighting. 0 is no lighting. + boolean use_light_angle; ///< When false, wall lighting is evenly distributed. When true, wall lighting is directional. + angle_t light_angle; ///< Angle of directional wall lighting. - // SRB2Kart: Keeps track of if a map lump exists, so we can tell when a map is being replaced. - boolean alreadyExists; + // Freed animal information + UINT8 numFlickies; ///< Internal. For freed flicky support. + mobjtype_t *flickies; ///< List of freeable flickies in this level. Allocated dynamically for space reasons. Be careful. - // Lua stuff. - // (This is not ifdeffed so the map header structure can stay identical, just in case.) - UINT8 numCustomOptions; ///< Internal. For Lua custom value support. - customoption_t *customopts; ///< Custom options. Allocated dynamically for space reasons. Be careful. + // Script information + char runsoc[33]; ///< SOC to execute at start of level (32 character limit instead of 63) + char scriptname[33]; ///< Script to use when the map is switched to. (32 character limit instead of 191) + + // Cutscene information + UINT8 precutscenenum; ///< Cutscene number to play BEFORE a level starts. + UINT8 cutscenenum; ///< Cutscene number to use, 0 for none. + + // Lua information + UINT8 numCustomOptions; ///< Internal. For Lua custom value support. + customoption_t *customopts; ///< Custom options. Allocated dynamically for space reasons. Be careful. } mapheader_t; // level flags @@ -432,28 +451,8 @@ typedef struct #define LF2_NOTIMEATTACK (1<<2) ///< Hide this map in Time Attack modes #define LF2_VISITNEEDED (1<<3) ///< Not available in Time Attack modes until you visit the level -extern mapheader_t* mapheaderinfo[NUMMAPS]; - -// This could support more, but is that a good idea? -// Keep in mind that it may encourage people making overly long cups just because they "can", and would be a waste of memory. -#define MAXLEVELLIST 5 - -typedef struct cupheader_s -{ - UINT16 id; ///< Cup ID - char name[15]; ///< Cup title (14 chars) - char icon[9]; ///< Name of the icon patch - INT16 levellist[MAXLEVELLIST]; ///< List of levels that belong to this cup - UINT8 numlevels; ///< Number of levels defined in levellist - INT16 bonusgame; ///< Map number to use for bonus game - INT16 specialstage; ///< Map number to use for special stage - UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald) - SINT8 unlockrequired; ///< An unlockable is required to select this cup. -1 for no unlocking required. - struct cupheader_s *next; ///< Next cup in linked list -} cupheader_t; - -extern cupheader_t *kartcupheaders; // Start of cup linked list -extern UINT16 numkartcupheaders; +extern mapheader_t** mapheaderinfo; +extern INT32 nummapheaders, mapallocsize; // Gametypes #define NUMGAMETYPEFREESLOTS 128 @@ -575,52 +574,10 @@ extern INT32 luabanks[NUM_LUABANKS]; extern INT32 nummaprings; //keep track of spawned rings/coins -/** Time attack information, currently a very small structure. - */ -typedef struct -{ - tic_t time; ///< Time in which the level was finished. - tic_t lap; ///< Best lap time for this level. - //UINT32 score; ///< Score when the level was finished. - //UINT16 rings; ///< Rings when the level was finished. -} recorddata_t; - -/** Setup for one NiGHTS map. - * These are dynamically allocated because I am insane - */ -#define GRADE_F 0 -#define GRADE_E 1 -#define GRADE_D 2 -#define GRADE_C 3 -#define GRADE_B 4 -#define GRADE_A 5 -#define GRADE_S 6 - -/*typedef struct -{ - // 8 mares, 1 overall (0) - UINT8 nummares; - UINT32 score[9]; - UINT8 grade[9]; - tic_t time[9]; -} nightsdata_t;*/ - -//extern nightsdata_t *nightsrecords[NUMMAPS]; -extern recorddata_t *mainrecords[NUMMAPS]; - -// mapvisited is now a set of flags that says what we've done in the map. -#define MV_VISITED (1) -#define MV_BEATEN (1<<1) -#define MV_ENCORE (1<<2) -#define MV_MAX (MV_VISITED|MV_BEATEN|MV_ENCORE) -#define MV_MP ((MV_MAX+1)<<1) -extern UINT8 mapvisited[NUMMAPS]; - extern UINT32 token; ///< Number of tokens collected in a level extern UINT32 tokenlist; ///< List of tokens collected extern boolean gottoken; ///< Did you get a token? Used for end of act extern INT32 tokenbits; ///< Used for setting token bits -extern INT32 sstimer; ///< Time allotted in the special stage extern UINT32 bluescore; ///< Blue Team Scores extern UINT32 redscore; ///< Red Team Scores diff --git a/src/f_finale.c b/src/f_finale.c index 05f07d5a4..88260f068 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1837,6 +1837,7 @@ static void F_CacheTitleScreen(void) void F_StartTitleScreen(void) { + INT32 titleMapNum; setup_numplayers = 0; if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS) @@ -1848,20 +1849,20 @@ void F_StartTitleScreen(void) else wipegamestate = GS_TITLESCREEN; - if (titlemap) + if (titlemap + && ((titleMapNum = G_MapNumber(titlemap)) < nummapheaders) + && mapheaderinfo[titleMapNum] + && mapheaderinfo[titleMapNum]->lumpnum != LUMPERROR) { mapthing_t *startpos; gamestate_t prevwipegamestate = wipegamestate; titlemapinaction = TITLEMAP_LOADING; titlemapcameraref = NULL; - gamemap = titlemap; + gamemap = titleMapNum+1; - if (!mapheaderinfo[gamemap-1]) - P_AllocMapHeader(gamemap-1); - - maptol = mapheaderinfo[gamemap-1]->typeoflevel; - globalweather = mapheaderinfo[gamemap-1]->weather; + maptol = mapheaderinfo[titleMapNum]->typeoflevel; + globalweather = mapheaderinfo[titleMapNum]->weather; G_DoLoadLevel(true); if (!titlemap) @@ -2196,12 +2197,14 @@ void F_TitleScreenTicker(boolean run) return; } +#ifdef STAFFGHOSTS // is it time? if (!(--demoIdleLeft)) { //static boolean use_netreplay = false; char dname[9]; + char *dname2 = dname; lumpnum_t l; const char *mapname; UINT8 numstaff; @@ -2231,41 +2234,16 @@ void F_TitleScreenTicker(boolean run) return; } - // Replay intro when done cycling through demos - /* - if (curDemo == numDemos) -- uuuh... we have a LOT of maps AND a big devteam... probably not gonna see a repeat unless you're super unlucky :V - { - curDemo = 0; - F_StartIntro(); - return; - } - */ - mapname = G_BuildMapName(G_RandMap(TOL_RACE, -2, 0, 0, false, NULL)+1); numstaff = 1; - while (numstaff < 99 && (l = W_CheckNumForName(va("%sS%02u",mapname,numstaff+1))) != LUMPERROR) + while (numstaff < 99 && (l = W_CheckNumForLongName(va("%sS%02u",mapname,numstaff+1))) != LUMPERROR) numstaff++; -#if 0 // turns out this isn't how we're gonna organise 'em - if (numstaff > 1) - { - if (laststaff && laststaff <= numstaff) // don't do the same staff member twice in a row, even if they're on different maps - { - numstaff = M_RandomKey(numstaff-1)+1; - if (numstaff >= laststaff) - numstaff++; - } - else - numstaff = M_RandomKey(numstaff)+1; - } - laststaff = numstaff; -#else numstaff = M_RandomKey(numstaff)+1; -#endif // Setup demo name - snprintf(dname, 9, "%sS%02u", mapname, numstaff); + dname2 = Z_StrDup(va("%sS%02u", mapname, numstaff)); /*if ((l = W_CheckNumForName(dname)) == LUMPERROR) -- we KNOW it exists now { @@ -2278,8 +2256,14 @@ loadreplay: demo.title = demo.fromtitle = true; demo.ignorefiles = true; demo.loadfiles = false; - G_DoPlayDemo(dname); + G_DoPlayDemo(dname2); + + if (dname2 != dname) + { + Z_Free(dname2); + } } +#endif //#ifdef STAFFGHOSTS } void F_TitleDemoTicker(void) @@ -2409,7 +2393,7 @@ void F_EndCutScene(void) F_StartGameEvaluation(); else if (cutnum == introtoplay-1) D_StartTitle(); - else if (nextmap < 1100-1) + else if (nextmap < NEXTMAP_SPECIAL) G_NextLevel(); else G_EndGame(); diff --git a/src/g_demo.c b/src/g_demo.c index 1e2d5e2f3..27e42e541 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -1994,7 +1994,7 @@ void G_BeginRecording(void) // game data M_Memcpy(demo_p, "PLAY", 4); demo_p += 4; - WRITEINT16(demo_p,gamemap); + WRITESTRINGN(demo_p, mapheaderinfo[gamemap-1]->lumpname, MAXMAPLUMPNAME); M_Memcpy(demo_p, mapmd5, 16); demo_p += 16; WRITEUINT8(demo_p, demoflags); @@ -2426,7 +2426,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) p += 16; // demo checksum I_Assert(!memcmp(p, "PLAY", 4)); p += 4; // PLAY - p += 2; // gamemap + SKIPSTRING(p); // gamemap p += 16; // map md5 flags = READUINT8(p); // demoflags p++; // gametype @@ -2484,7 +2484,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) Z_Free(buffer); return UINT8_MAX; } p += 4; // "PLAY" - p += 2; // gamemap + SKIPSTRING(p); // gamemap p += 16; // mapmd5 flags = READUINT8(p); p++; // gametype @@ -2703,7 +2703,7 @@ void G_DoPlayDemo(char *defdemoname) { UINT8 i, p; lumpnum_t l; - char skin[17],color[MAXCOLORNAME+1],follower[17],*n,*pdemoname; + char skin[17],color[MAXCOLORNAME+1],follower[17],mapname[MAXMAPLUMPNAME],*n,*pdemoname; UINT8 version,subversion; UINT32 randseed; char msg[1024]; @@ -2824,7 +2824,8 @@ void G_DoPlayDemo(char *defdemoname) return; } demo_p += 4; // "PLAY" - gamemap = READINT16(demo_p); + READSTRINGN(demo_p, mapname, sizeof(mapname)); // gamemap + gamemap = G_MapNumber(mapname)+1; demo_p += 16; // mapmd5 demoflags = READUINT8(demo_p); @@ -2918,7 +2919,7 @@ void G_DoPlayDemo(char *defdemoname) demo_p += 4; // Extrainfo location // ...*map* not loaded? - if (!gamemap || (gamemap > NUMMAPS) || !mapheaderinfo[gamemap-1] || !(mapheaderinfo[gamemap-1]->alreadyExists == true)) + if (!gamemap || (gamemap > nummapheaders) || !mapheaderinfo[gamemap-1] || mapheaderinfo[gamemap-1]->lumpnum == LUMPERROR) { snprintf(msg, 1024, M_GetText("%s features a course that is not currently loaded.\n"), pdemoname); CONS_Alert(CONS_ERROR, "%s", msg); @@ -3118,7 +3119,7 @@ void G_DoPlayDemo(char *defdemoname) R_ExecuteSetViewSize(); P_SetRandSeed(randseed); - G_InitNew(demoflags & DF_ENCORE, G_BuildMapName(gamemap), true, true, false); // Doesn't matter whether you reset or not here, given changes to resetplayer. + G_InitNew(demoflags & DF_ENCORE, gamemap, true, true, false); // Doesn't matter whether you reset or not here, given changes to resetplayer. for (i = 0; i < MAXPLAYERS; i++) { @@ -3233,7 +3234,7 @@ void G_AddGhost(char *defdemoname) } p += 4; // "PLAY" - p += 2; // gamemap + SKIPSTRING(p); // gamemap p += 16; // mapmd5 (possibly check for consistency?) flags = READUINT8(p); @@ -3358,7 +3359,7 @@ void G_AddGhost(char *defdemoname) ghosts = gh; gh->version = ghostversion; - mthing = playerstarts[0]; + mthing = playerstarts[0] ? playerstarts[0] : deathmatchstarts[0]; // todo not correct but out of scope I_Assert(mthing); { // A bit more complex than P_SpawnPlayer because ghosts aren't solid and won't just push themselves out of the ceiling. fixed_t z,f,c; @@ -3462,7 +3463,7 @@ void G_UpdateStaffGhostName(lumpnum_t l) } p += 4; // "PLAY" - p += 2; // gamemap + SKIPSTRING(p); // gamemap p += 16; // mapmd5 (possibly check for consistency?) flags = READUINT8(p); @@ -3540,6 +3541,7 @@ void G_DoPlayMetal(void) thinker_t *th; // it's an internal demo + // TODO: Use map header to determine lump name if ((l = W_CheckNumForName(va("%sMS",G_BuildMapName(gamemap)))) == LUMPERROR) { CONS_Alert(CONS_WARNING, M_GetText("No bot recording for this map.\n")); diff --git a/src/g_game.c b/src/g_game.c index 8cc5b1f72..01d32146d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -75,7 +75,11 @@ JoyType_t Joystick[MAXSPLITSCREENPLAYERS]; #define SAVEGAMESIZE (1024) // SRB2kart -char gamedatafilename[64] = "ringdata.dat"; +char gamedatafilename[64] = +#ifdef DEVELOP + "develop" +#endif + "ringdata.dat"; char timeattackfolder[64] = "ringracers"; char customversionstring[32] = "\0"; @@ -147,18 +151,14 @@ INT32 g_localplayers[MAXSPLITSCREENPLAYERS]; tic_t gametic; tic_t levelstarttic; // gametic at level start -UINT32 ssspheres; // old special stage INT16 lastmap; // last level you were at (returning from special stages) tic_t timeinmap; // Ticker for time spent in level (used for levelcard display) -INT16 spstage_start, spmarathon_start; -INT16 sstage_start, sstage_end, smpstage_start, smpstage_end; - -INT16 titlemap = 0; +char * titlemap = NULL; boolean hidetitlepics = false; -INT16 bootmap; //bootmap for loading a map on startup +char * bootmap = NULL; //bootmap for loading a map on startup -INT16 tutorialmap = 0; // map to load for tutorial +char * tutorialmap = NULL; // map to load for tutorial boolean tutorialmode = false; // are we in a tutorial right now? boolean looptitle = true; @@ -168,8 +168,6 @@ UINT16 skincolor_blueteam = SKINCOLOR_BLUE; UINT16 skincolor_redring = SKINCOLOR_RASPBERRY; UINT16 skincolor_bluering = SKINCOLOR_PERIWINKLE; -tic_t countdowntimer = 0; -boolean countdowntimeup = false; boolean exitfadestarted = false; cutscene_t *cutscenes[128]; @@ -188,7 +186,8 @@ mapthing_t *bflagpoint; struct quake quake; // Map Header Information -mapheader_t* mapheaderinfo[NUMMAPS] = {NULL}; +mapheader_t** mapheaderinfo = {NULL}; +INT32 nummapheaders, mapallocsize = 0; // Kart cup definitions cupheader_t *kartcupheaders = NULL; @@ -207,19 +206,10 @@ UINT32 tokenlist; // List of tokens collected boolean gottoken; // Did you get a token? Used for end of act INT32 tokenbits; // Used for setting token bits -// Old Special Stage -INT32 sstimer; // Time allotted in the special stage - tic_t totalplaytime; UINT32 matchesplayed; // SRB2Kart boolean gamedataloaded = false; -// Time attack data for levels -// These are dynamically allocated for space reasons now -recorddata_t *mainrecords[NUMMAPS] = {NULL}; -//nightsdata_t *nightsrecords[NUMMAPS] = {NULL}; -UINT8 mapvisited[NUMMAPS]; - // Temporary holding place for nights data for the current map //nightsdata_t ntemprecords; @@ -330,7 +320,25 @@ boolean legitimateexit; // Did this client actually finish the match? boolean comebackshowninfo; // Have you already seen the "ATTACK OR PROTECT" message? tic_t curlap; // Current lap time tic_t bestlap; // Best lap time -static INT16 randmapbuffer[NUMMAPS+1]; // Buffer for maps RandMap is allowed to roll + +typedef struct +{ + INT16 *mapbuffer; // Pointer to zone memory + INT32 lastnummapheaders; // Reset if nummapheaders != this + UINT8 counttogametype; // Time to gametype change event +} randmaps_t; +static randmaps_t randmaps = {NULL, 0, 0}; + +static void G_ResetRandMapBuffer(void) +{ + INT32 i; + Z_Free(randmaps.mapbuffer); + randmaps.lastnummapheaders = nummapheaders; + randmaps.mapbuffer = Z_Malloc(randmaps.lastnummapheaders * sizeof(INT16), PU_STATIC, NULL); + for (i = 0; i < randmaps.lastnummapheaders; i++) + randmaps.mapbuffer[i] = -1; + //intentionally not resetting randmaps.counttogametype here +} // Grading UINT32 timesBeaten; @@ -454,21 +462,23 @@ INT32 player_name_changes[MAXPLAYERS]; // Allocation for time and nights data void G_AllocMainRecordData(INT16 i) { - if (!mainrecords[i]) - mainrecords[i] = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL); - memset(mainrecords[i], 0, sizeof(recorddata_t)); + if (i > nummapheaders || !mapheaderinfo[i]) + I_Error("G_AllocMainRecordData: Internal map ID %d not found (nummapheaders = %d)\n", i, nummapheaders); + if (!mapheaderinfo[i]->mainrecord) + mapheaderinfo[i]->mainrecord = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL); + memset(mapheaderinfo[i]->mainrecord, 0, sizeof(recorddata_t)); } // MAKE SURE YOU SAVE DATA BEFORE CALLING THIS void G_ClearRecords(void) { INT16 i; - for (i = 0; i < NUMMAPS; ++i) + for (i = 0; i < nummapheaders; ++i) { - if (mainrecords[i]) + if (mapheaderinfo[i]->mainrecord) { - Z_Free(mainrecords[i]); - mainrecords[i] = NULL; + Z_Free(mapheaderinfo[i]->mainrecord); + mapheaderinfo[i]->mainrecord = NULL; } /*if (nightsrecords[i]) { @@ -481,20 +491,22 @@ void G_ClearRecords(void) // For easy retrieval of records tic_t G_GetBestTime(INT16 map) { - if (!mainrecords[map-1] || mainrecords[map-1]->time <= 0) + if (!mapheaderinfo[map] || !mapheaderinfo[map]->mainrecord || mapheaderinfo[map]->mainrecord->time <= 0) return (tic_t)UINT32_MAX; - return mainrecords[map-1]->time; + return mapheaderinfo[map]->mainrecord->time; } +// BE RIGHT BACK + // Not needed /* tic_t G_GetBestLap(INT16 map) { - if (!mainrecords[map-1] || mainrecords[map-1]->lap <= 0) + if (!mapheaderinfo[map] || !mapheaderinfo[map]->mainrecord || mapheaderinfo[map]->mainrecord->lap <= 0) return (tic_t)UINT32_MAX; - return mainrecords[map-1]->lap; + return mapheaderinfo[map]->mainrecord->lap; } */ @@ -511,7 +523,7 @@ static void G_UpdateRecordReplays(void) UINT8 earnedEmblems; // Record new best time - if (!mainrecords[gamemap-1]) + if (!mapheaderinfo[gamemap-1]->mainrecord) G_AllocMainRecordData(gamemap-1); if (players[consoleplayer].pflags & PF_NOCONTEST) @@ -519,20 +531,20 @@ static void G_UpdateRecordReplays(void) players[consoleplayer].realtime = UINT32_MAX; } - if (((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time)) + if (((mapheaderinfo[gamemap-1]->mainrecord->time == 0) || (players[consoleplayer].realtime < mapheaderinfo[gamemap-1]->mainrecord->time)) && (players[consoleplayer].realtime < UINT32_MAX)) // DNF { - mainrecords[gamemap-1]->time = players[consoleplayer].realtime; + mapheaderinfo[gamemap-1]->mainrecord->time = players[consoleplayer].realtime; } if (modeattacking == ATTACKING_TIME) { - if ((mainrecords[gamemap-1]->lap == 0) || (bestlap < mainrecords[gamemap-1]->lap)) - mainrecords[gamemap-1]->lap = bestlap; + if ((mapheaderinfo[gamemap-1]->mainrecord->lap == 0) || (bestlap < mapheaderinfo[gamemap-1]->mainrecord->lap)) + mapheaderinfo[gamemap-1]->mainrecord->lap = bestlap; } else { - mainrecords[gamemap-1]->lap = 0; + mapheaderinfo[gamemap-1]->mainrecord->lap = 0; } // Save demo! @@ -618,46 +630,61 @@ void G_SetGameModified(boolean silent, boolean major) Command_ExitGame_f(); } -/** Builds an original game map name from a map number. - * The complexity is due to MAPA0-MAPZZ. +/** Returns the map lump name for a map number. * * \param map Map number. - * \return Pointer to a static buffer containing the desired map name. - * \sa M_MapNumber + * \return Map name. + * \sa G_MapNumber */ const char *G_BuildMapName(INT32 map) { - static char mapname[10] = "MAPXX"; // internal map name (wad resource name) - - I_Assert(map > 0); - I_Assert(map <= NUMMAPS); - -#if 0 - if (map == 0) // hack??? + if (map > 0 && map <= nummapheaders && mapheaderinfo[map - 1] != NULL) { - if (gamestate == GS_TITLESCREEN) - map = -1; - else if (gamestate == GS_LEVEL) - map = gamemap-1; - else - map = prevmap; - map = G_RandMap(G_TOLFlag(cv_newgametype.value), map, 0, 0, false, NULL)+1; + return mapheaderinfo[map - 1]->lumpname; } -#endif - - if (map < 100) - sprintf(&mapname[3], "%.2d", map); else { - mapname[3] = (char)('A' + (char)((map - 100) / 36)); - if ((map - 100) % 36 < 10) - mapname[4] = (char)('0' + (char)((map - 100) % 36)); - else - mapname[4] = (char)('A' + (char)((map - 100) % 36) - 10); - mapname[5] = '\0'; + return NULL; + } +} + +/** Returns the map number for map lump name. + * + * \param name Map name; + * \return Map number. + * \sa G_BuildMapName, nextmapspecial_t + */ +INT32 G_MapNumber(const char * name) +{ +#ifdef NEXTMAPINSOC + if (strncasecmp("NEXTMAP_", name, 8) != 0) +#endif + { + INT32 map; + + for (map = 0; map < nummapheaders; ++map) + { + if (strcasecmp(mapheaderinfo[map]->lumpname, name) != 0) + continue; + + return map; + } + + return NEXTMAP_INVALID; } - return mapname; +#ifdef NEXTMAPINSOC + name += 8; + + if (strcasecmp("EVALUATION", name) == 0) + return NEXTMAP_EVALUATION; + if (strcasecmp("CREDITS", name) == 0) + return NEXTMAP_CREDITS; + if (strcasecmp("CEREMONY", name) == 0) + return NEXTMAP_CEREMONY; + //if (strcasecmp("TITLE", name) == 0) + return NEXTMAP_TITLE; +#endif } /** Clips the console player's mouse aiming to the current view. @@ -1306,9 +1333,11 @@ void G_DoLoadLevel(boolean resetplayer) // cleanup if (titlemapinaction == TITLEMAP_LOADING) { - if (W_CheckNumForName(G_BuildMapName(gamemap)) == LUMPERROR) + //if (W_CheckNumForName(G_BuildMapName(gamemap)) == LUMPERROR) + if (gamemap < 1 || gamemap > nummapheaders) { - titlemap = 0; // let's not infinite recursion ok + Z_Free(titlemap); + titlemap = NULL; // let's not infinite recursion ok Command_ExitGame_f(); return; } @@ -2958,7 +2987,7 @@ const char *Gametype_ConstantNames[NUMGAMETYPES] = UINT32 gametypedefaultrules[NUMGAMETYPES] = { // Race - GTR_CIRCUIT|GTR_BOTS, + GTR_CAMPAIGN|GTR_CIRCUIT|GTR_BOTS, // Battle GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME }; @@ -3159,18 +3188,15 @@ INT32 G_GetGametypeByName(const char *gametypestr) // boolean G_IsSpecialStage(INT32 mapnum) { -#if 1 - (void)mapnum; -#else - if (modeattacking == ATTACKING_TIME) - return false; - if (mapnum >= sstage_start && mapnum <= sstage_end) - return true; - if (mapnum >= smpstage_start && mapnum <= smpstage_end) - return true; -#endif + mapnum--; // gamemap-based to 0 indexed - return false; + if (mapnum > nummapheaders || !mapheaderinfo[mapnum]) + return false; + + if (!mapheaderinfo[mapnum]->cup || mapheaderinfo[mapnum]->cup->cachedlevels[CUPCACHE_SPECIAL] != mapnum) + return false; + + return true; } // @@ -3247,6 +3273,10 @@ INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype) && ((gametyperules|gametypedefaultrules[prefgametype]) & GTR_CIRCUIT)); UINT8 encoremodifier = 0; + // -- the below is only necessary if you want to use randmaps.mapbuffer here + //if (randmaps.lastnummapheaders != nummapheaders) + //G_ResetRandMapBuffer(); + if (encorepossible) { if (encorescramble != -1) @@ -3277,21 +3307,21 @@ INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype) if (!cv_kartvoterulechanges.value) // never return (gametype|encoremodifier); - if (randmapbuffer[NUMMAPS] > 0 && (cv_kartvoterulechanges.value != 3)) + if (randmaps.counttogametype > 0 && (cv_kartvoterulechanges.value != 3)) { - randmapbuffer[NUMMAPS]--; + randmaps.counttogametype--; return (gametype|encoremodifier); } switch (cv_kartvoterulechanges.value) // okay, we're having a gametype change! when's the next one, luv? { case 1: // sometimes - randmapbuffer[NUMMAPS] = 5; // per "cup" + randmaps.counttogametype = 5; // per "cup" break; default: // fallthrough - happens when clearing buffer, but needs a reasonable countdown if cvar is modified case 2: // frequent - randmapbuffer[NUMMAPS] = 2; // ...every 1/2th-ish cup? + randmaps.counttogametype = 2; // ...every 1/2th-ish cup? break; } @@ -3338,20 +3368,54 @@ UINT32 G_TOLFlag(INT32 pgametype) return gametypetol[pgametype]; } -static UINT32 TOLMaps(UINT32 tolflags) +INT16 G_GetFirstMapOfGametype(UINT8 pgametype) { - UINT32 num = 0; - UINT32 i; + INT16 mapnum = NEXTMAP_INVALID; - // Find all the maps that are ok and and put them in an array. - for (i = 0; i < NUMMAPS; i++) + if ((gametypedefaultrules[pgametype] & GTR_CAMPAIGN) && kartcupheaders) + { + mapnum = kartcupheaders->cachedlevels[0]; + } + + if (mapnum >= nummapheaders) + { + UINT32 tolflag = G_TOLFlag(pgametype); + for (mapnum = 0; mapnum < nummapheaders; mapnum++) + { + if (!mapheaderinfo[mapnum]) + continue; + if (mapheaderinfo[mapnum]->lumpnum == LUMPERROR) + continue; + if (!(mapheaderinfo[mapnum]->typeoflevel & tolflag)) + continue; + if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU) + continue; + break; + } + } + + return mapnum; +} + +static INT32 TOLMaps(UINT8 pgametype) +{ + INT32 num = 0; + INT32 i; + + UINT32 tolflag = G_TOLFlag(pgametype); + + // Find all the maps that are ok + for (i = 0; i < nummapheaders; i++) { if (!mapheaderinfo[i]) continue; + if (mapheaderinfo[i]->lumpnum == LUMPERROR) + continue; + if (!(mapheaderinfo[i]->typeoflevel & tolflag)) + continue; if (mapheaderinfo[i]->menuflags & LF2_HIDEINMENU) // Don't include Map Hell continue; - if ((mapheaderinfo[i]->typeoflevel & tolflags) == tolflags) - num++; + num++; } return num; @@ -3373,10 +3437,13 @@ INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, UINT8 ignorebuffer, UINT8 maphe UINT16 extbufsize = 0; boolean usehellmaps; // Only consider Hell maps in this pick + if (randmaps.lastnummapheaders != nummapheaders) + G_ResetRandMapBuffer(); + if (!okmaps) { //CONS_Printf("(making okmaps)\n"); - okmaps = Z_Malloc(NUMMAPS * sizeof(INT16), PU_STATIC, NULL); + okmaps = Z_Malloc(nummapheaders * sizeof(INT16), PU_STATIC, NULL); } if (extbuffer != NULL) @@ -3393,11 +3460,11 @@ tryagain: usehellmaps = (maphell == 0 ? false : (maphell == 2 || M_RandomChance(FRACUNIT/100))); // 1% chance of Hell // Find all the maps that are ok and and put them in an array. - for (ix = 0; ix < NUMMAPS; ix++) + for (ix = 0; ix < nummapheaders; ix++) { boolean isokmap = true; - if (!mapheaderinfo[ix]) + if (!mapheaderinfo[ix] || mapheaderinfo[ix]->lumpnum == LUMPERROR) continue; if ((mapheaderinfo[ix]->typeoflevel & tolflags) != tolflags @@ -3425,11 +3492,11 @@ tryagain: continue; } - for (bufx = 0; bufx < (maphell ? 3 : NUMMAPS); bufx++) + for (bufx = 0; bufx < (maphell ? 3 : randmaps.lastnummapheaders); bufx++) { - if (randmapbuffer[bufx] == -1) // Rest of buffer SHOULD be empty + if (randmaps.mapbuffer[bufx] == -1) // Rest of buffer SHOULD be empty break; - if (ix == randmapbuffer[bufx]) + if (ix == randmaps.mapbuffer[bufx]) { isokmap = false; break; @@ -3440,12 +3507,15 @@ tryagain: continue; } +#ifdef STAFFGHOSTS if (pprevmap == -2) // title demo hack { lumpnum_t l; - if ((l = W_CheckNumForName(va("%sS01",G_BuildMapName(ix+1)))) == LUMPERROR) + // TODO: Use map header to determine lump name + if ((l = W_CheckNumForLongName(va("%sS01",G_BuildMapName(ix+1)))) == LUMPERROR) continue; } +#endif //#ifdef STAFFGHOSTS okmaps[numokmaps++] = ix; } @@ -3454,15 +3524,15 @@ tryagain: { if (!ignorebuffer) { - if (randmapbuffer[3] == -1) // Is the buffer basically empty? + if (randmaps.mapbuffer[3] == -1) // Is the buffer basically empty? { ignorebuffer = 1; // This will probably only help in situations where there's very few maps, but it's folly not to at least try it //CONS_Printf("RANDMAP - ignoring buffer\n"); goto tryagain; } - for (bufx = 3; bufx < NUMMAPS; bufx++) // Let's clear all but the three most recent maps... - randmapbuffer[bufx] = -1; + for (bufx = 3; bufx < randmaps.lastnummapheaders; bufx++) // Let's clear all but the three most recent maps... + randmaps.mapbuffer[bufx] = -1; //CONS_Printf("RANDMAP - emptying randmapbuffer\n"); goto tryagain; } @@ -3479,8 +3549,8 @@ tryagain: if (ignorebuffer == 1) { //CONS_Printf("(emptying randmapbuffer entirely)\n"); - for (bufx = 0; bufx < NUMMAPS; bufx++) - randmapbuffer[bufx] = -1; // if we're having trouble finding a map we should probably clear it + for (bufx = 0; bufx < randmaps.lastnummapheaders; bufx++) + randmaps.mapbuffer[bufx] = -1; // if we're having trouble finding a map we should probably clear it } } else @@ -3501,19 +3571,30 @@ tryagain: void G_AddMapToBuffer(INT16 map) { - INT16 bufx, refreshnum = max(0, ((INT32)TOLMaps(G_TOLFlag(gametype)))-3); + INT16 bufx; + INT16 refreshnum = (TOLMaps(gametype))-3; - // Add the map to the buffer. - for (bufx = NUMMAPS-1; bufx > 0; bufx--) - randmapbuffer[bufx] = randmapbuffer[bufx-1]; - randmapbuffer[0] = map; + if (refreshnum < 0) + refreshnum = 3; + + if (nummapheaders != randmaps.lastnummapheaders) + { + G_ResetRandMapBuffer(); + } + else + { + for (bufx = randmaps.lastnummapheaders-1; bufx > 0; bufx--) + randmaps.mapbuffer[bufx] = randmaps.mapbuffer[bufx-1]; + } + + randmaps.mapbuffer[0] = map; // We're getting pretty full, so lets flush this for future usage. - if (randmapbuffer[refreshnum] != -1) + if (randmaps.mapbuffer[refreshnum] != -1) { // Clear all but the five most recent maps. - for (bufx = 5; bufx < NUMMAPS; bufx++) // bufx < refreshnum? Might not handle everything for gametype switches, though. - randmapbuffer[bufx] = -1; + for (bufx = 5; bufx < randmaps.lastnummapheaders; bufx++) + randmaps.mapbuffer[bufx] = -1; //CONS_Printf("Random map buffer has been flushed.\n"); } } @@ -3523,20 +3604,19 @@ void G_AddMapToBuffer(INT16 map) // static void G_UpdateVisited(void) { - boolean spec = G_IsSpecialStage(gamemap); // Update visitation flags? - if ((!modifiedgame || savemoddata) // Not modified - && !multiplayer && !demo.playback // SP/RA/NiGHTS mode - && !(spec && stagefailed)) // Not failed the special stage + if (/*(!majormods || savemoddata) // Not modified + &&*/ !multiplayer && !demo.playback // SP/RA/NiGHTS mode + && !(modeattacking && (players[consoleplayer].pflags & PF_NOCONTEST))) // Not failed { UINT8 earnedEmblems; // Update visitation flags - mapvisited[gamemap-1] |= MV_BEATEN; + mapheaderinfo[gamemap-1]->mapvisited |= MV_BEATEN; if (encoremode == true) { - mapvisited[gamemap-1] |= MV_ENCORE; + mapheaderinfo[gamemap-1]->mapvisited |= MV_ENCORE; } if (modeattacking) @@ -3557,7 +3637,7 @@ static boolean CanSaveLevel(INT32 mapnum) static void G_HandleSaveLevel(void) { // do this before running the intermission or custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c - if (nextmap >= 1100-1) + if (nextmap >= NEXTMAP_SPECIAL) { if (!gamecomplete) gamecomplete = 2; // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission @@ -3571,7 +3651,7 @@ static void G_HandleSaveLevel(void) cursaveslot = 0; } else if ((!modifiedgame || savemoddata) && !(netgame || multiplayer || ultimatemode || demo.recording || metalrecording || modeattacking)) - G_SaveGame((UINT32)cursaveslot, spstage_start); + G_SaveGame((UINT32)cursaveslot, 0); // TODO when we readd a campaign one day } } // and doing THIS here means you don't lose your progress if you close the game mid-intermission @@ -3580,6 +3660,170 @@ static void G_HandleSaveLevel(void) G_SaveGame((UINT32)cursaveslot, lastmap+1); // not nextmap+1 to route around special stages } +static void G_GetNextMap(void) +{ + INT32 i; + + // go to next level + // nextmap is 0-based, unlike gamemap + if (nextmapoverride != 0) + { + nextmap = (INT16)(nextmapoverride-1); + } + else if (grandprixinfo.gp == true) + { + if (grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session + { + nextmap = prevmap; // Same map + } + else + { + if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map + { + nextmap = NEXTMAP_CEREMONY; // ceremonymap + } + else + { + // Proceed to next map + const INT32 cupLevelNum =grandprixinfo.cup->cachedlevels[grandprixinfo.roundnum]; + + if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]) + { + nextmap = cupLevelNum; + } + else + { + nextmap = prevmap; // Prevent uninitialised use + } + + grandprixinfo.roundnum++; + } + } + } + else if (bossinfo.boss == true) + { + nextmap = NEXTMAP_TITLE; // temporary + } + else + { + UINT32 tolflag = G_TOLFlag(gametype); + register INT16 cm; + + if (gametyperules & GTR_CAMPAIGN) + { + cupheader_t *cup = mapheaderinfo[gamemap-1]->cup; + UINT8 gettingresult = 0; + + while (cup) + { + // Not unlocked? Grab the next result afterwards + if (!marathonmode && cup->unlockrequired != -1 && !unlockables[cup->unlockrequired].unlocked) + { + cup = cup->next; + gettingresult = 1; + continue; + } + + for (i = 0; i < cup->numlevels; i++) + { + cm = cup->cachedlevels[i]; + + // Not valid? + if (cm >= nummapheaders + || !mapheaderinfo[cm] + || mapheaderinfo[cm]->lumpnum == LUMPERROR + || !(mapheaderinfo[cm]->typeoflevel & tolflag) + || (!marathonmode && M_MapLocked(cm+1))) + continue; + + // Grab the first valid after the map you're on + if (gettingresult) + { + nextmap = cm; + gettingresult = 2; + break; + } + + // Not the map you're on? + if (cm != prevmap) + { + continue; + } + + // Ok, this is the current map, time to get the next + gettingresult = 1; + } + + // We have a good nextmap? + if (gettingresult == 2) + { + break; + } + + // Ok, iterate to the next + cup = cup->next; + } + + // Didn't get a nextmap before reaching the end? + if (gettingresult != 2) + { + if (marathonmode) + { + nextmap = NEXTMAP_CEREMONY; // ceremonymap + } + else + { + nextmap = NEXTMAP_TITLE; + } + } + } + else + { + cm = prevmap; + if (++cm >= nummapheaders) + cm = 0; + + while (cm != prevmap) + { + if (!mapheaderinfo[cm] + || mapheaderinfo[cm]->lumpnum == LUMPERROR + || !(mapheaderinfo[cm]->typeoflevel & tolflag) + || (mapheaderinfo[cm]->menuflags & LF2_HIDEINMENU) + || M_MapLocked(cm+1)) + { + if (++cm >= nummapheaders) + cm = 0; + continue; + } + + break; + } + + nextmap = cm; + } + + if (!marathonmode) + { + if (cv_advancemap.value == 0) // Stay on same map. + { + nextmap = prevmap; + } + else if (cv_advancemap.value == 2) // Go to random map. + { + nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, false, NULL); + } + else if (nextmap >= NEXTMAP_SPECIAL) // Loop back around + { + nextmap = G_GetFirstMapOfGametype(gametype); + } + } + } + + // We are committed to this map now. + if (nextmap == NEXTMAP_INVALID || (nextmap < NEXTMAP_SPECIAL && (nextmap >= nummapheaders || !mapheaderinfo[nextmap] || mapheaderinfo[nextmap]->lumpnum == LUMPERROR))) + I_Error("G_GetNextMap: Internal map ID %d not found (nummapheaders = %d)\n", nextmap, nummapheaders); +} + // // G_DoCompleted // @@ -3641,138 +3885,24 @@ static void G_DoCompleted(void) prevmap = (INT16)(gamemap-1); - if (demo.playback) goto demointermission; - - // go to next level - // nextmap is 0-based, unlike gamemap - if (nextmapoverride != 0) + if (!demo.playback) { - nextmap = (INT16)(nextmapoverride-1); - } - else if (marathonmode && mapheaderinfo[gamemap-1]->marathonnext) - { - nextmap = (INT16)(mapheaderinfo[gamemap-1]->marathonnext-1); - } - else if (grandprixinfo.gp == true) - { - if (grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session - { - nextmap = prevmap; // Same map - } - else - { - if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map - { - nextmap = 1101; // ceremonymap - } - else - { - // Proceed to next map - nextmap = grandprixinfo.cup->levellist[grandprixinfo.roundnum]; - grandprixinfo.roundnum++; - } - } - } - else - { - nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1); - if (marathonmode && nextmap == spmarathon_start-1) - nextmap = 1100-1; // No infinite loop for you - } - - // Remember last map for when you come out of the special stage. - if (!spec) - lastmap = nextmap; - - // If nextmap is actually going to get used, make sure it points to - // a map of the proper gametype -- skip levels that don't support - // the current gametype. (Helps avoid playing boss levels in Race, - // for instance). - if (!modeattacking && grandprixinfo.gp == false && bossinfo.boss == false) - { - if (nextmap >= 0 && nextmap < NUMMAPS) - { - register INT16 cm = nextmap; - UINT32 tolflag = G_TOLFlag(gametype); - UINT8 visitedmap[(NUMMAPS+7)/8]; - - memset(visitedmap, 0, sizeof (visitedmap)); - - while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag)) - { - visitedmap[cm/8] |= (1<<(cm&7)); - if (!mapheaderinfo[cm]) - cm = -1; // guarantee error execution - else if (marathonmode && mapheaderinfo[cm]->marathonnext) - cm = (INT16)(mapheaderinfo[cm]->marathonnext-1); - else - cm = (INT16)(mapheaderinfo[cm]->nextlevel-1); - - if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error) - { - cm = nextmap; //Start the loop again so that the error checking below is executed. - - //Make sure the map actually exists before you try to go to it! - if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR)) - { - CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1); - cm = 0; - break; - } - } - - if (visitedmap[cm/8] & (1<<(cm&7))) // smells familiar - { - // We got stuck in a loop, came back to the map we started on - // without finding one supporting the current gametype. - // Thus, print a warning, and just use this map anyways. - CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1); - break; - } - } - nextmap = cm; - } - - // wrap around in race - if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN)) - nextmap = (INT16)(spstage_start-1); - - if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1) - I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1); + G_GetNextMap(); + // Remember last map for when you come out of the special stage. if (!spec) - lastmap = nextmap; // Remember last map for when you come out of the special stage. + lastmap = nextmap; + + // Set up power level gametype scrambles + K_SetPowerLevelScrambles(powertype); } - automapactive = false; - - if (!(gametyperules & GTR_CAMPAIGN)) - { - if (cv_advancemap.value == 0) // Stay on same map. - { - nextmap = prevmap; - } - else if (cv_advancemap.value == 2) // Go to random map. - { - nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, false, NULL); - } - } - - // We are committed to this map now. - // We may as well allocate its header if it doesn't exist - // (That is, if it's a real map) - if (nextmap < NUMMAPS && !mapheaderinfo[nextmap]) - P_AllocMapHeader(nextmap); - - // Set up power level gametype scrambles - K_SetPowerLevelScrambles(powertype); - -demointermission: - // If the current gametype has no intermission screen set, then don't start it. Y_DetermineIntermissionType(); - if ((skipstats && !modeattacking) || (spec && modeattacking && stagefailed) || (intertype == int_none)) + if ((skipstats && !modeattacking) + || (modeattacking && (players[consoleplayer].pflags & PF_NOCONTEST)) + || (intertype == int_none)) { G_UpdateVisited(); G_HandleSaveLevel(); @@ -3790,17 +3920,10 @@ demointermission: // See also F_EndCutscene, the only other place which handles intra-map/ending transitions void G_AfterIntermission(void) { - if (modeattacking) - { - M_EndModeAttackRun(); - return; - } - if (gamecomplete == 2) // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission gamecomplete = 1; HU_ClearCEcho(); - //G_NextLevel(); if (demo.playback) { @@ -3824,11 +3947,11 @@ void G_AfterIntermission(void) return; } - if ((gametyperules & GTR_CAMPAIGN) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene. - F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false); + if ((gametyperules & GTR_CAMPAIGN) && mapheaderinfo[prevmap]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene. + F_StartCustomCutscene(mapheaderinfo[prevmap]->cutscenenum-1, false, false); else { - if (nextmap < 1100-1) + if (nextmap < NEXTMAP_SPECIAL) G_NextLevel(); else G_EndGame(); @@ -3969,27 +4092,27 @@ void G_EndGame(void) if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) G_SaveDemo(); - // Only do evaluation and credits in coop games. - if (gametyperules & GTR_CAMPAIGN) + // Only do evaluation and credits in singleplayer contexts + if (!netgame && (gametyperules & GTR_CAMPAIGN)) { - if (nextmap == 1103-1) // end game with ending + if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony { - F_StartEnding(); + D_StartTitle(); //F_StartEnding(); -- temporary return; } - if (nextmap == 1102-1) // end game with credits + if (nextmap == NEXTMAP_CREDITS) // end game with credits { F_StartCredits(); return; } - if (nextmap == 1101-1) // end game with evaluation + if (nextmap == NEXTMAP_EVALUATION) // end game with evaluation { F_StartGameEvaluation(); return; } } - // 1100 or competitive multiplayer, so go back to title screen. + // direct or competitive multiplayer, so go back to title screen. D_StartTitle(); } @@ -3999,30 +4122,23 @@ void G_EndGame(void) // Sets a tad of default info we need. void G_LoadGameSettings(void) { - // defaults - spstage_start = spmarathon_start = 1; - sstage_start = 50; - sstage_end = 56; // 7 special stages in vanilla SRB2 - sstage_end++; // plus one weirdo - smpstage_start = 60; - smpstage_end = 66; // 7 multiplayer special stages too - // initialize free sfx slots for skin sounds S_InitRuntimeSounds(); } +#define GD_VERSIONCHECK 0xBA5ED444 + // G_LoadGameData // Loads the main data file, which stores information such as emblems found, etc. void G_LoadGameData(void) { size_t length; - INT32 i, j; + UINT32 i, j; UINT8 modded = false; UINT8 rtemp; //For records - tic_t rectime; - tic_t reclap; + UINT32 numgamedatamapheaders; // Clear things so previously read gamedata doesn't transfer // to new gamedata @@ -4053,7 +4169,7 @@ void G_LoadGameData(void) save_p = savebuffer; // Version check - if (READUINT32(save_p) != 0xFCAFE211) + if (READUINT32(save_p) != GD_VERSIONCHECK) { const char *gdfolder = "the Ring Racers folder"; if (strcmp(srb2home,".")) @@ -4075,11 +4191,6 @@ void G_LoadGameData(void) else if (modded != true && modded != false) goto datacorrupt; - // TODO put another cipher on these things? meh, I don't care... - for (i = 0; i < NUMMAPS; i++) - if ((mapvisited[i] = READUINT8(save_p)) > MV_MAX) - goto datacorrupt; - // To save space, use one bit per collected/achieved/unlocked flag for (i = 0; i < MAXEMBLEMS;) { @@ -4113,16 +4224,44 @@ void G_LoadGameData(void) timesBeaten = READUINT32(save_p); // Main records - for (i = 0; i < NUMMAPS; ++i) + numgamedatamapheaders = READUINT32(save_p); + if (numgamedatamapheaders >= NEXTMAP_SPECIAL) + goto datacorrupt; + + for (i = 0; i < numgamedatamapheaders; i++) { + char mapname[MAXMAPLUMPNAME]; + INT16 mapnum; + tic_t rectime; + tic_t reclap; + + READSTRINGN(save_p, mapname, sizeof(mapname)); + mapnum = G_MapNumber(mapname); + + rtemp = READUINT8(save_p); rectime = (tic_t)READUINT32(save_p); reclap = (tic_t)READUINT32(save_p); - if (rectime || reclap) + if (mapnum < nummapheaders && mapheaderinfo[mapnum]) { - G_AllocMainRecordData((INT16)i); - mainrecords[i]->time = rectime; - mainrecords[i]->lap = reclap; + // Valid mapheader, time to populate with record data. + + if ((mapheaderinfo[mapnum]->mapvisited = rtemp) & ~MV_MAX) + goto datacorrupt; + + if (rectime || reclap) + { + G_AllocMainRecordData((INT16)i); + mapheaderinfo[i]->mainrecord->time = rectime; + mapheaderinfo[i]->mainrecord->lap = reclap; + CONS_Printf("ID %d, Time = %d, Lap = %d\n", i, rectime/35, reclap/35); + } + } + else + { + // Since it's not worth declaring the entire gamedata + // corrupt over extra maps, we report and move on. + CONS_Alert(CONS_WARNING, "Map with lumpname %s does not exist, time record data will be discarded\n", mapname); } } @@ -4160,7 +4299,10 @@ void G_SaveGameData(void) if (!gamedataloaded) return; // If never loaded (-nodata), don't save - save_p = savebuffer = (UINT8 *)malloc(GAMEDATASIZE); + length = (4+4+4+1+(MAXEMBLEMS)+MAXEXTRAEMBLEMS+MAXUNLOCKABLES+MAXCONDITIONSETS+4+4); + length += nummapheaders * (MAXMAPLUMPNAME+1+4+4); + + save_p = savebuffer = (UINT8 *)malloc(length); if (!save_p) { CONS_Alert(CONS_ERROR, M_GetText("No more free memory for saving game data\n")); @@ -4178,19 +4320,15 @@ void G_SaveGameData(void) #endif // Version test - WRITEUINT32(save_p, 0xFCAFE211); + WRITEUINT32(save_p, GD_VERSIONCHECK); // 4 - WRITEUINT32(save_p, totalplaytime); - WRITEUINT32(save_p, matchesplayed); + WRITEUINT32(save_p, totalplaytime); // 4 + WRITEUINT32(save_p, matchesplayed); // 4 - WRITEUINT8(save_p, (UINT8)savemoddata); - - // TODO put another cipher on these things? meh, I don't care... - for (i = 0; i < NUMMAPS; i++) - WRITEUINT8(save_p, (mapvisited[i] & MV_MAX)); + WRITEUINT8(save_p, (UINT8)savemoddata); // 1 // To save space, use one bit per collected/achieved/unlocked flag - for (i = 0; i < MAXEMBLEMS;) + for (i = 0; i < MAXEMBLEMS;) // MAXEMBLEMS * 1; { btemp = 0; for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j) @@ -4198,7 +4336,7 @@ void G_SaveGameData(void) WRITEUINT8(save_p, btemp); i += j; } - for (i = 0; i < MAXEXTRAEMBLEMS;) + for (i = 0; i < MAXEXTRAEMBLEMS;) // MAXEXTRAEMBLEMS * 1; { btemp = 0; for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j) @@ -4206,7 +4344,7 @@ void G_SaveGameData(void) WRITEUINT8(save_p, btemp); i += j; } - for (i = 0; i < MAXUNLOCKABLES;) + for (i = 0; i < MAXUNLOCKABLES;) // MAXUNLOCKABLES * 1; { btemp = 0; for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j) @@ -4214,7 +4352,7 @@ void G_SaveGameData(void) WRITEUINT8(save_p, btemp); i += j; } - for (i = 0; i < MAXCONDITIONSETS;) + for (i = 0; i < MAXCONDITIONSETS;) // MAXCONDITIONSETS * 1; { btemp = 0; for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j) @@ -4223,22 +4361,28 @@ void G_SaveGameData(void) i += j; } - WRITEUINT32(save_p, timesBeaten); + WRITEUINT32(save_p, timesBeaten); // 4 // Main records - for (i = 0; i < NUMMAPS; i++) + WRITEUINT32(save_p, nummapheaders); // 4 + + for (i = 0; i < nummapheaders; i++) // nummapheaders * (255+1+4+4) { - if (mainrecords[i]) + // For figuring out which header to assing it to on load + WRITESTRINGN(save_p, mapheaderinfo[i]->lumpname, MAXMAPLUMPNAME); + + WRITEUINT8(save_p, (mapheaderinfo[i]->mapvisited & MV_MAX)); + + if (mapheaderinfo[i]->mainrecord) { - WRITEUINT32(save_p, mainrecords[i]->time); - WRITEUINT32(save_p, mainrecords[i]->lap); + WRITEUINT32(save_p, mapheaderinfo[i]->mainrecord->time); + WRITEUINT32(save_p, mapheaderinfo[i]->mainrecord->lap); } else { WRITEUINT32(save_p, 0); WRITEUINT32(save_p, 0); } - WRITEUINT8(save_p, 0); // compat } length = save_p - savebuffer; @@ -4519,9 +4663,8 @@ cleanup: // Can be called by the startup code or the menu task, // consoleplayer, displayplayers[], playeringame[] should be set. // -void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar, UINT8 ssplayers, boolean FLS) +void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ssplayers, boolean FLS) { - INT32 i; UINT16 color = SKINCOLOR_NONE; paused = false; @@ -4531,8 +4674,7 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar G_FreeGhosts(); // TODO: do we actually need to do this? - for (i = 0; i < NUMMAPS+1; i++) - randmapbuffer[i] = -1; + G_ResetRandMapBuffer(); // this leave the actual game if needed SV_StartSinglePlayerServer(); @@ -4551,18 +4693,17 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar CV_StealthSetValue(&cv_playercolor[0], color); } - if (mapname) - { - D_MapChange(M_MapNumber(mapname[3], mapname[4]), gametype, pencoremode, true, 1, false, FLS); - } + D_MapChange(map, gametype, pencoremode, true, 1, false, FLS); } // // This is the map command interpretation something like Command_Map_f // // called at: map cmd execution, doloadgame, doplaydemo -void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, boolean skipprecutscene, boolean FLS) +void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skipprecutscene, boolean FLS) { + const char * mapname = G_BuildMapName(map); + INT32 i; (void)FLS; @@ -4573,7 +4714,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool S_ResumeAudio(); } - prevencoremode = ((gamestate == GS_TITLESCREEN) ? false : encoremode); + prevencoremode = ((!Playing()) ? false : encoremode); encoremode = pencoremode; legitimateexit = false; // SRB2Kart @@ -4615,19 +4756,20 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool // internal game map // well this check is useless because it is done before (d_netcmd.c::command_map_f) // but in case of for demos.... - if (W_CheckNumForName(mapname) == LUMPERROR) + if (!mapname) + { + I_Error("Internal game map with ID %d not found\n", map); + Command_ExitGame_f(); + return; + } + if (mapheaderinfo[map-1]->lumpnum == LUMPERROR) { I_Error("Internal game map '%s' not found\n", mapname); Command_ExitGame_f(); return; } - gamemap = (INT16)M_MapNumber(mapname[3], mapname[4]); // get xx out of MAPxx - - // gamemap changed; we assume that its map header is always valid, - // so make it so - if(!mapheaderinfo[gamemap-1]) - P_AllocMapHeader(gamemap-1); + gamemap = map; maptol = mapheaderinfo[gamemap-1]->typeoflevel; globalweather = mapheaderinfo[gamemap-1]->weather; @@ -4650,7 +4792,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool { char *title = G_BuildMapTitle(gamemap); - CON_LogMessage(va(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap))); + CON_LogMessage(va(M_GetText("Map is now \"%s"), mapname)); if (title) { CON_LogMessage(va(": %s", title)); @@ -4665,11 +4807,8 @@ char *G_BuildMapTitle(INT32 mapnum) { char *title = NULL; - if (mapnum == 0) - return Z_StrDup("Random"); - - if (!mapheaderinfo[mapnum-1]) - P_AllocMapHeader(mapnum-1); + if (!mapnum || mapnum > nummapheaders || !mapheaderinfo[mapnum-1]) + I_Error("G_BuildMapTitle: Internal map ID %d not found (nummapheaders = %d)", mapnum-1, nummapheaders); if (strcmp(mapheaderinfo[mapnum-1]->lvlttl, "")) { @@ -4765,21 +4904,16 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep, mapnamelen = strlen(mapname); - /* Count available maps; how ugly. */ - for (i = 0, freqc = 0; i < NUMMAPS; ++i) - { - if (mapheaderinfo[i]) - freqc++; - } - - freq = ZZ_Calloc(freqc * sizeof (mapsearchfreq_t)); + freq = ZZ_Calloc(nummapheaders * sizeof (mapsearchfreq_t)); wanttable = !!( freqp ); freqc = 0; - for (i = 0, mapnum = 1; i < NUMMAPS; ++i, ++mapnum) - if (mapheaderinfo[i]) + for (i = 0, mapnum = 1; i < nummapheaders; ++i, ++mapnum) { + if (!mapheaderinfo[i] || mapheaderinfo[i]->lumpnum == LUMPERROR) + continue; + if (!( realmapname = G_BuildMapTitle(mapnum) )) continue; @@ -4888,55 +5022,30 @@ void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc) INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) { - boolean usemapcode = false; - INT32 newmapnum; - size_t mapnamelen; - char *p; - mapnamelen = strlen(mapname); + /* Now detect map number in base 10, which no one asked for. */ + newmapnum = strtol(mapname, &p, 10); - if (mapnamelen == 2)/* maybe two digit code */ + if (*p == '\0')/* we got it */ { - if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) )) - usemapcode = true; - } - else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0) - { - if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) )) - usemapcode = true; - } - - if (!usemapcode) - { - /* Now detect map number in base 10, which no one asked for. */ - newmapnum = strtol(mapname, &p, 10); - if (*p == '\0')/* we got it */ - { - if (newmapnum < 1 || newmapnum > NUMMAPS) - { - CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum); - return 0; - } - usemapcode = true; - } - else - { - newmapnum = G_FindMap(mapname, realmapnamep, NULL, NULL); - } - } - - if (usemapcode) - { - /* we can't check mapheaderinfo for this hahahaha */ - if (W_CheckNumForName(G_BuildMapName(newmapnum)) == LUMPERROR) + if (newmapnum < 1 || newmapnum > nummapheaders) + return 0; + if (!mapheaderinfo[newmapnum-1] || mapheaderinfo[newmapnum-1]->lumpnum == LUMPERROR) return 0; - - if (realmapnamep) - (*realmapnamep) = G_BuildMapTitle(newmapnum); } + else + { + newmapnum = G_MapNumber(mapname)+1; + + if (newmapnum > nummapheaders) + return G_FindMap(mapname, realmapnamep, NULL, NULL); + } + + if (realmapnamep) + (*realmapnamep) = G_BuildMapTitle(newmapnum); return newmapnum; } diff --git a/src/g_game.h b/src/g_game.h index 4341a36c3..fbd423963 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -23,7 +23,6 @@ extern char gamedatafilename[64]; extern char timeattackfolder[64]; extern char customversionstring[32]; -#define GAMEDATASIZE (4*8192) extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; extern INT32 player_name_changes[MAXPLAYERS]; @@ -36,6 +35,19 @@ extern tic_t levelstarttic; // for modding? extern INT16 prevmap, nextmap; + +// see also G_MapNumber +typedef enum +{ + NEXTMAP_RESERVED = INT16_MAX, // so nextmap+1 doesn't roll over -- remove when gamemap is made 0-indexed + NEXTMAP_TITLE = INT16_MAX-1, + NEXTMAP_EVALUATION = INT16_MAX-2, + NEXTMAP_CREDITS = INT16_MAX-3, + NEXTMAP_CEREMONY = INT16_MAX-4, + NEXTMAP_INVALID = INT16_MAX-5, // Always last (swap with NEXTMAP_RESERVED when removing that) + NEXTMAP_SPECIAL = NEXTMAP_INVALID +} nextmapspecial_t; + extern INT32 gameovertics; extern UINT8 ammoremovaltics; extern tic_t timeinmap; // Ticker for time spent in level (used for levelcard display) @@ -81,8 +93,8 @@ extern consvar_t cv_resume; #define MAXPLMOVE (50) #define SLOWTURNTICS (6) -// build an internal map name MAPxx from map number const char *G_BuildMapName(INT32 map); +INT32 G_MapNumber(const char *mapname); void G_ResetAnglePrediction(player_t *player); void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer); @@ -112,7 +124,7 @@ boolean G_PlayerInputDown(UINT8 p, INT32 gc, UINT8 menuPlayers); void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo); void G_DoReborn(INT32 playernum); void G_PlayerReborn(INT32 player, boolean betweenmaps); -void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, +void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skipprecutscene, boolean FLS); char *G_BuildMapTitle(INT32 mapnum); @@ -150,7 +162,7 @@ void G_SpawnPlayer(INT32 playernum); // Can be called by the startup code or M_Responder. // A normal game starts at map 1, but a warp test can start elsewhere -void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar, +void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ssplayers, boolean FLS); void G_DoLoadLevel(boolean resetplayer); @@ -244,6 +256,7 @@ FUNCMATH INT32 G_TicsToMilliseconds(tic_t tics); // Don't split up TOL handling UINT32 G_TOLFlag(INT32 pgametype); +INT16 G_GetFirstMapOfGametype(UINT8 pgametype); INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, UINT8 ignorebuffer, UINT8 maphell, boolean callagainsoon, INT16 *extbuffer); void G_AddMapToBuffer(INT16 map); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 1d7f23ac9..f74efdfb3 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -98,6 +98,7 @@ static char hu_tick; //------------------------------------------- patch_t *missingpat; +patch_t *blanklvl; // song credits static patch_t *songcreditbg; @@ -186,6 +187,8 @@ void HU_LoadGraphics(void) Font_Load(); + HU_UpdatePatch(&blanklvl, "BLANKLVL"); + HU_UpdatePatch(&songcreditbg, "K_SONGCR"); // cache ping gfx: diff --git a/src/k_battle.c b/src/k_battle.c index 14ecfabf5..e8afc580f 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -17,6 +17,7 @@ #include "s_sound.h" #include "m_random.h" #include "r_sky.h" // skyflatnum +#include "k_grandprix.h" // K_CanChangeRules // Battle overtime info struct battleovertime battleovertime; @@ -126,7 +127,7 @@ void K_CheckBumpers(void) winnerscoreadd -= players[i].roundscore; } - if (bossinfo.boss) + if (K_CanChangeRules() == false) { if (nobumpers) { diff --git a/src/k_hud.c b/src/k_hud.c index 9f14d882a..14e526dd2 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -3320,7 +3320,6 @@ static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 static void K_drawKartMinimap(void) { - INT32 lumpnum; patch_t *AutomapPic; INT32 i = 0; INT32 x, y; @@ -3344,12 +3343,12 @@ static void K_drawKartMinimap(void) if (stplyr != &players[displayplayers[0]]) return; - lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(gamemap))); + AutomapPic = mapheaderinfo[gamemap-1]->minimapPic; - if (lumpnum != -1) - AutomapPic = W_CachePatchName(va("%sR", G_BuildMapName(gamemap)), PU_HUDGFX); - else + if (!AutomapPic) + { return; // no pic, just get outta here + } if (r_splitscreen < 2) // 1/2P right aligned { diff --git a/src/k_menu.h b/src/k_menu.h index caf21013b..a55756788 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -218,6 +218,15 @@ extern menu_t PLAY_LevelSelectDef; extern menuitem_t PLAY_TimeAttack[]; extern menu_t PLAY_TimeAttackDef; +typedef enum +{ + ta_replay = 0, + ta_guest, + ta_ghosts, + ta_spacer, + ta_start, +} ta_e; + extern menuitem_t PLAY_TAReplay[]; extern menu_t PLAY_TAReplayDef; diff --git a/src/k_menudef.c b/src/k_menudef.c index 97f77abd2..59036b51c 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -204,6 +204,7 @@ menu_t PLAY_LevelSelectDef = { NULL }; +// see ta_e menuitem_t PLAY_TimeAttack[] = { {IT_STRING | IT_SUBMENU, "Replay...", NULL, NULL, {.submenu = &PLAY_TAReplayDef}, 0, 0}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 2785f83ac..9e18e181f 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1846,14 +1846,16 @@ static void M_DrawCupPreview(INT16 y, cupheader_t *cup) i = (cupgrid.previewanim / 82) % cup->numlevels; while (x < BASEVIDWIDTH + pad) { - lumpnum_t lumpnum; - patch_t *PictureOfLevel; + INT32 cupLevelNum = cup->cachedlevels[i]; + patch_t *PictureOfLevel = NULL; - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cup->levellist[i]+1))); - if (lumpnum != LUMPERROR) - PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); - else - PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); + if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]) + { + PictureOfLevel = mapheaderinfo[cupLevelNum]->thumbnailPic; + } + + if (!PictureOfLevel) + PictureOfLevel = blanklvl; V_DrawSmallScaledPatch(x + 1, y+2, 0, PictureOfLevel); i = (i+1) % cup->numlevels; @@ -2073,18 +2075,19 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) static void M_DrawLevelSelectBlock(INT16 x, INT16 y, INT16 map, boolean redblink, boolean greyscale) { - lumpnum_t lumpnum; - patch_t *PictureOfLevel; + patch_t *PictureOfLevel = NULL; UINT8 *colormap = NULL; if (greyscale) colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE); - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(map+1))); - if (lumpnum != LUMPERROR) - PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); - else - PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); + if (mapheaderinfo[map]) + { + PictureOfLevel = mapheaderinfo[map]->thumbnailPic; + } + + if (!PictureOfLevel) + PictureOfLevel = blanklvl; if (redblink) V_DrawScaledPatch(3+x, y, 0, W_CachePatchName("LVLSEL2", PU_CACHE)); @@ -2114,10 +2117,10 @@ void M_DrawLevelSelect(void) { INT16 lvlx = t, lvly = y; - while (!M_CanShowLevelInList(map, levellist.newgametype) && map < NUMMAPS) + while (!M_CanShowLevelInList(map, levellist.newgametype) && map < nummapheaders) map++; - if (map >= NUMMAPS) + if (map >= nummapheaders) break; if (i == levellist.cursor && tatransition) @@ -2146,7 +2149,7 @@ void M_DrawTimeAttack(void) INT16 rightedge = 149+t+155; INT16 opty = 140; INT32 w; - lumpnum_t lumpnum; + patch_t *minimap = NULL; UINT8 i; consvar_t *cv; @@ -2156,17 +2159,34 @@ void M_DrawTimeAttack(void) V_DrawScaledPatch(149+t, 70, 0, W_CachePatchName("BESTTIME", PU_CACHE)); - if (currentMenu == &PLAY_TimeAttackDef) + if (currentMenu == &PLAY_TimeAttackDef && mapheaderinfo[map]) { - lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(map+1))); - if (lumpnum != LUMPERROR) - V_DrawScaledPatch(24-t, 82, 0, W_CachePatchNum(lumpnum, PU_CACHE)); + tic_t timerec = 0; + tic_t laprec = 0; + UINT32 timeheight = 82; - V_DrawRightAlignedString(rightedge-12, 82, highlightflags, "BEST LAP:"); - K_drawKartTimestamp(0, 162+t, 88, 0, 2); + if ((minimap = mapheaderinfo[map]->minimapPic)) + V_DrawScaledPatch(24-t, 82, 0, minimap); - V_DrawRightAlignedString(rightedge-12, 112, highlightflags, "BEST TIME:"); - K_drawKartTimestamp(0, 162+t, 118, map, 1); + if (mapheaderinfo[map]->mainrecord) + { + timerec = mapheaderinfo[map]->mainrecord->time; + laprec = mapheaderinfo[map]->mainrecord->lap; + } + + if (levellist.newgametype != GT_BATTLE) + { + V_DrawRightAlignedString(rightedge-12, timeheight, highlightflags, "BEST LAP:"); + K_drawKartTimestamp(laprec, 162+t, timeheight+6, 0, 2); + timeheight += 30; + } + else + { + timeheight += 15; + } + + V_DrawRightAlignedString(rightedge-12, timeheight, highlightflags, "BEST TIME:"); + K_drawKartTimestamp(timerec, 162+t, timeheight+6, map, 1); } else opty = 80; @@ -3789,8 +3809,7 @@ void M_DrawPlaybackMenu(void) #define SCALEDVIEWHEIGHT (vid.height/vid.dupy) void M_DrawReplayHutReplayInfo(void) { - lumpnum_t lumpnum; - patch_t *patch; + patch_t *patch = NULL; UINT8 *colormap; INT32 x, y, w, h; @@ -3816,11 +3835,19 @@ void M_DrawReplayHutReplayInfo(void) // A 160x100 image of the level as entry MAPxxP //CONS_Printf("%d %s\n", extrasmenu.demolist[dir_on[menudepthleft]].map, G_BuildMapName(extrasmenu.demolist[dir_on[menudepthleft]].map)); - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(extrasmenu.demolist[dir_on[menudepthleft]].map))); - if (lumpnum != LUMPERROR) - patch = W_CachePatchNum(lumpnum, PU_CACHE); - else + + if (mapheaderinfo[extrasmenu.demolist[dir_on[menudepthleft]].map]) + { + patch = mapheaderinfo[extrasmenu.demolist[dir_on[menudepthleft]].map]->thumbnailPic; + if (!patch) + { + patch = blanklvl; + } + } + else if (!patch) + { patch = W_CachePatchName("M_NOLVL", PU_CACHE); + } if (!(extrasmenu.demolist[dir_on[menudepthleft]].kartspeed & DF_ENCORE)) V_DrawSmallScaledPatch(x, y, V_SNAPTOTOP, patch); diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 4ee347587..9d57fa6ea 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -249,7 +249,7 @@ static void Dummymenuplayer_OnChange(void) static void Dummystaff_OnChange(void) { -#if 0 +#ifdef STAFFGHOSTS lumpnum_t l; dummystaffname[0] = '\0'; @@ -281,7 +281,7 @@ static void Dummystaff_OnChange(void) sprintf(temp, " - %d", cv_dummystaff.value); } -#endif +#endif //#ifdef STAFFGHOSTS } void Screenshot_option_Onchange(void) @@ -3182,6 +3182,8 @@ void M_SetupDifficultySelect(INT32 choice) // boolean M_CanShowLevelInList(INT16 mapnum, UINT8 gt) { + UINT32 tolflag = G_TOLFlag(gt); + // Does the map exist? if (!mapheaderinfo[mapnum]) return false; @@ -3190,48 +3192,40 @@ boolean M_CanShowLevelInList(INT16 mapnum, UINT8 gt) if (!mapheaderinfo[mapnum]->lvlttl[0]) return false; + // Does the map have a LUMP? + if (mapheaderinfo[mapnum]->lumpnum == LUMPERROR) + return false; + if (M_MapLocked(mapnum+1)) return false; // not unlocked + // Check for TOL + if (!(mapheaderinfo[mapnum]->typeoflevel & tolflag)) + return false; + // Should the map be hidden? - if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU /*&& mapnum+1 != gamemap*/) + if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU) return false; // I don't know why, but some may have exceptions. if (levellist.timeattack && (mapheaderinfo[mapnum]->menuflags & LF2_NOTIMEATTACK)) return false; - if (gt == GT_BATTLE && (mapheaderinfo[mapnum]->typeoflevel & TOL_BATTLE)) - return true; - - if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) + if (gametypedefaultrules[gt] & GTR_CAMPAIGN && levellist.selectedcup) { - if (levellist.selectedcup && levellist.selectedcup->numlevels) - { - UINT8 i; - - for (i = 0; i < levellist.selectedcup->numlevels; i++) - { - if (mapnum == levellist.selectedcup->levellist[i]) - break; - } - - if (i == levellist.selectedcup->numlevels) - return false; - } - - return true; + if (mapheaderinfo[mapnum]->cup != levellist.selectedcup) + return false; } - // Hmm? Couldn't decide? - return false; + // Survived our checks. + return true; } INT16 M_CountLevelsToShowInList(UINT8 gt) { INT16 mapnum, count = 0; - for (mapnum = 0; mapnum < NUMMAPS; mapnum++) + for (mapnum = 0; mapnum < nummapheaders; mapnum++) if (M_CanShowLevelInList(mapnum, gt)) count++; @@ -3242,7 +3236,7 @@ INT16 M_GetFirstLevelInList(UINT8 gt) { INT16 mapnum; - for (mapnum = 0; mapnum < NUMMAPS; mapnum++) + for (mapnum = 0; mapnum < nummapheaders; mapnum++) if (M_CanShowLevelInList(mapnum, gt)) return mapnum; @@ -3410,7 +3404,9 @@ void M_CupSelectHandler(INT32 choice) { M_SetMenuDelay(pid); - if ((!newcup) || (newcup && newcup->unlockrequired != -1 && !unlockables[newcup->unlockrequired].unlocked)) + if ((!newcup) + || (newcup && newcup->unlockrequired != -1 && !unlockables[newcup->unlockrequired].unlocked) + || (newcup->cachedlevels[0] == NEXTMAP_INVALID)) { S_StartSound(NULL, sfx_s3kb2); return; @@ -3418,7 +3414,7 @@ void M_CupSelectHandler(INT32 choice) if (cupgrid.grandprix == true) { - + INT32 levelNum; UINT8 ssplayers = cv_splitplayers.value-1; S_StartSound(NULL, sfx_s3k63); @@ -3462,8 +3458,10 @@ void M_CupSelectHandler(INT32 choice) netgame = levellist.netgame; // ^ ditto. } + levelNum = grandprixinfo.cup->cachedlevels[0]; + D_MapChange( - grandprixinfo.cup->levellist[0] + 1, + levelNum + 1, GT_RACE, grandprixinfo.encore, true, @@ -3549,16 +3547,16 @@ void M_LevelSelectHandler(INT32 choice) { map++; - while (!M_CanShowLevelInList(map, levellist.newgametype) && map < NUMMAPS) + while (!M_CanShowLevelInList(map, levellist.newgametype) && map < nummapheaders) map++; - if (map >= NUMMAPS) + if (map >= nummapheaders) break; add--; } - if (map >= NUMMAPS) + if (map >= nummapheaders) { // This shouldn't happen return; diff --git a/src/lua_baselib.c b/src/lua_baselib.c index f63bb85ef..771d41b51 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -33,7 +33,6 @@ #include "k_hud.h" #include "d_netcmd.h" // IsPlayerAdmin #include "k_menu.h" // Player Setup menu color stuff -#include "m_misc.h" // M_MapNumber #include "p_spec.h" // P_StartQuake #include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision @@ -378,23 +377,6 @@ static int lib_pGetEffectiveFollowerColor(lua_State *L) return 1; } -// M_MISC -////////////// - -static int lib_mMapNumber(lua_State *L) -{ - const char *arg = luaL_checkstring(L, 1); - size_t len = strlen(arg); - if (len == 2 || len == 5) { - char first = arg[len-2]; - char second = arg[len-1]; - lua_pushinteger(L, M_MapNumber(first, second)); - } else { - lua_pushinteger(L, 0); - } - return 1; -} - // M_RANDOM ////////////// @@ -2499,9 +2481,8 @@ static int lib_sStopSoundByID(lua_State *L) static int lib_sChangeMusic(lua_State *L) { - UINT32 position, prefadems, fadeinms; - const char *music_name = luaL_checkstring(L, 1); + UINT32 position, prefadems, fadeinms; boolean looping = (boolean)lua_opttrueboolean(L, 2); player_t *player = NULL; UINT16 music_flags = 0; @@ -3065,12 +3046,12 @@ static int lib_gBuildMapTitle(lua_State *L) { INT32 map = Lcheckmapnumber(L, 1, "G_BuildMapTitle"); char *name; - if (map < 1 || map > NUMMAPS) + if (map < 1 || map > nummapheaders) { return luaL_error(L, - "map number %d out of range (1 - %d)", + "map ID %d out of range (1 - %d)", map, - NUMMAPS + nummapheaders ); } name = G_BuildMapTitle(map); @@ -3838,9 +3819,6 @@ static luaL_Reg lib[] = { {"M_GetColorAfter",lib_pGetColorAfter}, {"M_GetColorBefore",lib_pGetColorBefore}, - // m_misc - {"M_MapNumber",lib_mMapNumber}, - // m_random {"P_RandomFixed",lib_pRandomFixed}, {"P_RandomByte",lib_pRandomByte}, diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index c8d0d3b10..ffacf34c5 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -574,6 +574,7 @@ static int libd_drawStretched(lua_State *L) } // KART: draw patch on minimap from x, y coordinates on the map +// Sal: Let's please just merge the relevant info into the actual function, and have Lua call that... static int libd_drawOnMinimap(lua_State *L) { fixed_t x, y, scale; // coordinates of the object @@ -583,7 +584,6 @@ static int libd_drawOnMinimap(lua_State *L) huddrawlist_h list; // variables used to replicate k_kart's mmap drawer: - INT32 lumpnum; patch_t *AutomapPic; INT32 mx, my; INT32 splitflags, minimaptrans; @@ -669,12 +669,12 @@ static int libd_drawOnMinimap(lua_State *L) if (stplyr != &players[displayplayers[0]]) return 0; - lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(gamemap))); + AutomapPic = mapheaderinfo[gamemap-1]->minimapPic; - if (lumpnum != -1) - AutomapPic = W_CachePatchName(va("%sR", G_BuildMapName(gamemap)), PU_HUDGFX); - else + if (!AutomapPic) + { return 0; // no pic, just get outta here + } mx = MM_X - (AutomapPic->width/2); my = MM_Y - (AutomapPic->height/2); diff --git a/src/lua_maplib.c b/src/lua_maplib.c index e537160da..16e26069f 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -2085,8 +2085,8 @@ static int lib_getMapheaderinfo(lua_State *L) lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) { - size_t i = lua_tointeger(L, 1)-1; - if (i >= NUMMAPS) + INT32 i = lua_tointeger(L, 1)-1; + if (i < 0 || i >= nummapheaders) return 0; LUA_PushUserdata(L, mapheaderinfo[i], META_MAPHEADER); //CONS_Printf(mapheaderinfo[i]->lvlttl); @@ -2104,7 +2104,7 @@ static int lib_getMapheaderinfo(lua_State *L) static int lib_nummapheaders(lua_State *L) { - lua_pushinteger(L, NUMMAPS); + lua_pushinteger(L, nummapheaders); return 1; } @@ -2116,7 +2116,6 @@ static int mapheaderinfo_get(lua_State *L) { mapheader_t *header = *((mapheader_t **)luaL_checkudata(L, 1, META_MAPHEADER)); const char *field = luaL_checkstring(L, 2); - INT16 i; if (fastcmp(field,"lvlttl")) lua_pushstring(L, header->lvlttl); else if (fastcmp(field,"subttl")) @@ -2127,10 +2126,6 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->actnum); else if (fastcmp(field,"typeoflevel")) lua_pushinteger(L, header->typeoflevel); - else if (fastcmp(field,"nextlevel")) - lua_pushinteger(L, header->nextlevel); - else if (fastcmp(field,"marathonnext")) - lua_pushinteger(L, header->marathonnext); else if (fastcmp(field,"keywords")) lua_pushstring(L, header->keywords); else if (fastcmp(field,"musname")) @@ -2139,22 +2134,6 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->mustrack); else if (fastcmp(field,"muspos")) lua_pushinteger(L, header->muspos); - else if (fastcmp(field,"musinterfadeout")) - lua_pushinteger(L, header->musinterfadeout); - else if (fastcmp(field,"musintername")) - lua_pushstring(L, header->musintername); - else if (fastcmp(field,"muspostbossname")) - lua_pushstring(L, header->muspostbossname); - else if (fastcmp(field,"muspostbosstrack")) - lua_pushinteger(L, header->muspostbosstrack); - else if (fastcmp(field,"muspostbosspos")) - lua_pushinteger(L, header->muspostbosspos); - else if (fastcmp(field,"muspostbossfadein")) - lua_pushinteger(L, header->muspostbossfadein); - else if (fastcmp(field,"musforcereset")) - lua_pushinteger(L, header->musforcereset); - else if (fastcmp(field,"forcecharacter")) - lua_pushstring(L, header->forcecharacter); else if (fastcmp(field,"weather")) lua_pushinteger(L, header->weather); else if (fastcmp(field,"skytexture")) @@ -2165,12 +2144,7 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->skybox_scaley); else if (fastcmp(field,"skybox_scalez")) lua_pushinteger(L, header->skybox_scalez); - else if (fastcmp(field,"interscreen")) { - for (i = 0; i < 8; i++) - if (!header->interscreen[i]) - break; - lua_pushlstring(L, header->interscreen, i); - } else if (fastcmp(field,"runsoc")) + else if (fastcmp(field,"runsoc")) lua_pushstring(L, header->runsoc); else if (fastcmp(field,"scriptname")) lua_pushstring(L, header->scriptname); @@ -2178,8 +2152,6 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->precutscenenum); else if (fastcmp(field,"cutscenenum")) lua_pushinteger(L, header->cutscenenum); - else if (fastcmp(field,"countdown")) - lua_pushinteger(L, header->countdown); else if (fastcmp(field,"palette")) lua_pushinteger(L, header->palette); else if (fastcmp(field,"numlaps")) @@ -2188,31 +2160,14 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->unlockrequired); else if (fastcmp(field,"levelselect")) lua_pushinteger(L, header->levelselect); - else if (fastcmp(field,"bonustype")) - lua_pushinteger(L, header->bonustype); - else if (fastcmp(field,"ltzzpatch")) - lua_pushstring(L, header->ltzzpatch); - else if (fastcmp(field,"ltzztext")) - lua_pushstring(L, header->ltzztext); - else if (fastcmp(field,"ltactdiamond")) - lua_pushstring(L, header->ltactdiamond); - else if (fastcmp(field,"maxbonuslives")) - lua_pushinteger(L, header->maxbonuslives); else if (fastcmp(field,"levelflags")) lua_pushinteger(L, header->levelflags); else if (fastcmp(field,"menuflags")) lua_pushinteger(L, header->menuflags); else if (fastcmp(field,"mobj_scale")) lua_pushfixed(L, header->mobj_scale); - else if (fastcmp(field,"startrings")) - lua_pushinteger(L, header->startrings); - else if (fastcmp(field, "sstimer")) - lua_pushinteger(L, header->sstimer); - else if (fastcmp(field, "ssspheres")) - lua_pushinteger(L, header->ssspheres); else if (fastcmp(field, "gravity")) lua_pushfixed(L, header->gravity); - // TODO add support for reading numGradedMares and grades else { // Read custom vars now // (note: don't include the "LUA." in your lua scripts!) diff --git a/src/lua_script.c b/src/lua_script.c index 051739976..f8fc119fd 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -211,35 +211,17 @@ int LUA_PushGlobals(lua_State *L, const char *word) lua_pushinteger(L, cv_pointlimit.value); return 1; // begin map vars - } else if (fastcmp(word,"spstage_start")) { - lua_pushinteger(L, spstage_start); - return 1; - } else if (fastcmp(word,"spmarathon_start")) { - lua_pushinteger(L, spmarathon_start); - return 1; - } else if (fastcmp(word,"sstage_start")) { - lua_pushinteger(L, sstage_start); - return 1; - } else if (fastcmp(word,"sstage_end")) { - lua_pushinteger(L, sstage_end); - return 1; - } else if (fastcmp(word,"smpstage_start")) { - lua_pushinteger(L, smpstage_start); - return 1; - } else if (fastcmp(word,"smpstage_end")) { - lua_pushinteger(L, smpstage_end); - return 1; } else if (fastcmp(word,"titlemap")) { - lua_pushinteger(L, titlemap); + lua_pushstring(L, titlemap); return 1; } else if (fastcmp(word,"titlemapinaction")) { lua_pushboolean(L, (titlemapinaction != TITLEMAP_OFF)); return 1; } else if (fastcmp(word,"bootmap")) { - lua_pushinteger(L, bootmap); + lua_pushstring(L, bootmap); return 1; } else if (fastcmp(word,"tutorialmap")) { - lua_pushinteger(L, tutorialmap); + lua_pushstring(L, tutorialmap); return 1; } else if (fastcmp(word,"tutorialmode")) { lua_pushboolean(L, tutorialmode); diff --git a/src/m_cond.c b/src/m_cond.c index 0760275f2..4dd42edca 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -81,7 +81,10 @@ void M_ClearSecrets(void) { INT32 i; - memset(mapvisited, 0, sizeof(mapvisited)); + for (i = 0; i < nummapheaders; ++i) + { + mapheaderinfo[i]->mapvisited = 0; + } for (i = 0; i < MAXEMBLEMS; ++i) emblemlocations[i].collected = false; @@ -129,11 +132,19 @@ UINT8 M_CheckCondition(condition_t *cn) case UC_OVERALLTIME: // Requires overall time <= x return (M_GotLowEnoughTime(cn->requirement)); case UC_MAPVISITED: // Requires map x to be visited - return ((mapvisited[cn->requirement - 1] & MV_VISITED) == MV_VISITED); case UC_MAPBEATEN: // Requires map x to be beaten - return ((mapvisited[cn->requirement - 1] & MV_BEATEN) == MV_BEATEN); case UC_MAPENCORE: // Requires map x to be beaten in encore - return ((mapvisited[cn->requirement - 1] & MV_ENCORE) == MV_ENCORE); + { + UINT8 mvtype = MV_VISITED; + if (cn->type == UC_MAPBEATEN) + mvtype = MV_BEATEN; + else if (cn->type == UC_MAPENCORE) + mvtype = MV_ENCORE; + + return ((cn->requirement < nummapheaders) + && (mapheaderinfo[cn->requirement]) + && ((mapheaderinfo[cn->requirement]->mapvisited & mvtype) == mvtype)); + } case UC_MAPTIME: // Requires time on map <= x return (G_GetBestTime(cn->extrainfo1) <= (unsigned)cn->requirement); case UC_TRIGGER: // requires map trigger set @@ -298,10 +309,17 @@ UINT8 M_CheckLevelEmblems(void) // Update Score, Time, Rings emblems for (i = 0; i < numemblems; ++i) { + INT32 checkLevel; + if (emblemlocations[i].type < ET_TIME || emblemlocations[i].collected) continue; - levelnum = emblemlocations[i].level; + checkLevel = G_MapNumber(emblemlocations[i].level); + + if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel]) + continue; + + levelnum = checkLevel; valToReach = emblemlocations[i].var; switch (emblemlocations[i].type) @@ -331,17 +349,24 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa for (i = 0; i < numemblems; ++i) { - if (emblemlocations[i].type != ET_MAP || emblemlocations[i].collected) + INT32 checkLevel; + + if (emblemlocations[i].type < ET_TIME || emblemlocations[i].collected) continue; - levelnum = emblemlocations[i].level; + checkLevel = G_MapNumber(emblemlocations[i].level); + + if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel]) + continue; + + levelnum = checkLevel; embtype = emblemlocations[i].var; flags = MV_BEATEN; if (embtype & ME_ENCORE) flags |= MV_ENCORE; - res = ((mapvisited[levelnum - 1] & flags) == flags); + res = ((mapheaderinfo[levelnum]->mapvisited & flags) == flags); emblemlocations[i].collected = res; if (res) @@ -407,7 +432,8 @@ UINT8 M_MapLocked(INT32 mapnum) if (1) return false; #endif - + if (!mapnum || mapnum > nummapheaders) + return false; if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0) return false; if (!unlockables[mapheaderinfo[mapnum-1]->unlockrequired].unlocked) @@ -458,14 +484,14 @@ UINT8 M_GotLowEnoughTime(INT32 tictime) INT32 curtics = 0; INT32 i; - for (i = 0; i < NUMMAPS; ++i) + for (i = 0; i < nummapheaders; ++i) { if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & LF2_NOTIMEATTACK)) continue; - if (!mainrecords[i] || !mainrecords[i]->time) + if (!mapheaderinfo[i]->mainrecord || !mapheaderinfo[i]->mainrecord->time) return false; - else if ((curtics += mainrecords[i]->time) > tictime) + else if ((curtics += mapheaderinfo[i]->mainrecord->time) > tictime) return false; } return true; @@ -492,7 +518,12 @@ emblem_t *M_GetLevelEmblems(INT32 mapnum) while (--i >= 0) { - if (emblemlocations[i].level == map) + INT32 checkLevel = G_MapNumber(emblemlocations[i].level); + + if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel]) + continue; + + if (checkLevel == map) return &emblemlocations[i]; } return NULL; diff --git a/src/m_cond.h b/src/m_cond.h index ed29fe326..323c84347 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -73,7 +73,7 @@ typedef struct { UINT8 type; ///< Emblem type INT16 tag; ///< Tag of emblem mapthing - INT16 level; ///< Level on which this emblem can be found. + char * level; ///< Level on which this emblem can be found. UINT8 sprite; ///< emblem sprite to use, 0 - 25 UINT16 color; ///< skincolor to use INT32 var; ///< If needed, specifies information on the target amount to achieve (or target skin) diff --git a/src/m_misc.c b/src/m_misc.c index 0628ef3b6..e8f9687cc 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -226,32 +226,6 @@ void M_AddToJoinedIPs(char *address, char *servname) strcpy(joinedIPlist[0][1], servname); } -/** Returns the map number for a map identified by the last two characters in - * its name. - * - * \param first The first character after MAP. - * \param second The second character after MAP. - * \return The map number, or 0 if no map corresponds to these characters. - * \sa G_BuildMapName - */ -INT32 M_MapNumber(char first, char second) -{ - if (isdigit(first)) - { - if (isdigit(second)) - return ((INT32)first - '0') * 10 + ((INT32)second - '0'); - return 0; - } - - if (!isalpha(first)) - return 0; - if (!isalnum(second)) - return 0; - - return 100 + ((INT32)tolower(first) - 'a') * 36 + (isdigit(second) ? ((INT32)second - '0') : - ((INT32)tolower(second) - 'a') + 10); -} - // ========================================================================== // FILE INPUT / OUTPUT // ========================================================================== @@ -956,9 +930,11 @@ static void M_PNGText(png_structp png_ptr, png_infop png_info_ptr, PNG_CONST png break; } +#if 0 if (gamestate == GS_LEVEL) snprintf(maptext, 8, "%s", G_BuildMapName(gamemap)); else +#endif snprintf(maptext, 8, "Unknown"); if (gamestate == GS_LEVEL && mapheaderinfo[gamemap-1]->lvlttl[0] != '\0') diff --git a/src/m_misc.h b/src/m_misc.h index 7c12fe841..12f51edcf 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -61,8 +61,6 @@ void M_AddToJoinedIPs(char *address, char *servname); void M_SaveJoinedIPs(void); void M_LoadJoinedIPs(void); -INT32 M_MapNumber(char first, char second); - boolean FIL_WriteFile(char const *name, const void *source, size_t length); size_t FIL_ReadFileTag(char const *name, UINT8 **buffer, INT32 tag); #define FIL_ReadFile(n, b) FIL_ReadFileTag(n, b, PU_STATIC) diff --git a/src/p_enemy.c b/src/p_enemy.c index 86403fa28..4cf7a7ad2 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3460,28 +3460,6 @@ void A_BossDeath(mobj_t *mo) EV_DoElevator(&junk, elevateUp, false); Tag_FSet(&junk.tags, LE_CAPSULE2); EV_DoElevator(&junk, elevateHighest, false); - - if (mapheaderinfo[gamemap-1]->muspostbossname[0] && - S_MusicExists(mapheaderinfo[gamemap-1]->muspostbossname)) - { - // Touching the egg trap button calls P_DoPlayerExit, which calls P_RestoreMusic. - // So just park ourselves in the mapmus variables. - // But don't change the mapmus variables if they were modified from their level header values (e.g., TUNES). - boolean changed = strnicmp(mapheaderinfo[gamemap-1]->musname, S_MusicName(), 7); - if (!strnicmp(mapheaderinfo[gamemap-1]->musname, mapmusname, 7)) - { - strncpy(mapmusname, mapheaderinfo[gamemap-1]->muspostbossname, 7); - mapmusname[6] = 0; - mapmusflags = (mapheaderinfo[gamemap-1]->muspostbosstrack & MUSIC_TRACKMASK) | MUSIC_RELOADRESET; - mapmusposition = mapheaderinfo[gamemap-1]->muspostbosspos; - } - - // don't change if we're in another tune - // but in case we're in jingle, use our parked mapmus variables so the correct track restores - if (!changed) - S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, (1*MUSICRATE)+(MUSICRATE/2), - mapheaderinfo[gamemap-1]->muspostbossfadein); - } } bossjustdie: diff --git a/src/p_mobj.c b/src/p_mobj.c index e8178b2dd..9c7b48aca 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11069,7 +11069,7 @@ void P_SpawnPlayer(INT32 playernum) else if (p->bot) { /* - if (bonusgame || specialstage) + if (bonusgame || specialstage || boss) { // Bots should avoid p->spectator = true; diff --git a/src/p_saveg.c b/src/p_saveg.c index a70ac6257..b638b03c9 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4427,8 +4427,8 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride) // gamemap changed; we assume that its map header is always valid, // so make it so - if(!mapheaderinfo[gamemap-1]) - P_AllocMapHeader(gamemap-1); + if (!gamemap || gamemap > nummapheaders || !mapheaderinfo[gamemap-1]) + I_Error("P_UnArchiveSPGame: Internal map ID %d not found (nummapheaders = %d)", gamemap-1, nummapheaders); //lastmapsaved = gamemap; lastmaploaded = gamemap; @@ -4482,7 +4482,6 @@ static void P_NetArchiveMisc(boolean resending) WRITEUINT8(save_p, encoremode); WRITEUINT32(save_p, leveltime); - WRITEUINT32(save_p, ssspheres); WRITEINT16(save_p, lastmap); WRITEUINT16(save_p, bossdisabled); @@ -4508,7 +4507,6 @@ static void P_NetArchiveMisc(boolean resending) } WRITEUINT32(save_p, token); - WRITEINT32(save_p, sstimer); WRITEUINT32(save_p, bluescore); WRITEUINT32(save_p, redscore); @@ -4537,9 +4535,6 @@ static void P_NetArchiveMisc(boolean resending) WRITEFIXED(save_p, gravity); WRITEFIXED(save_p, mapobjectscale); - WRITEUINT32(save_p, countdowntimer); - WRITEUINT8(save_p, countdowntimeup); - // SRB2kart WRITEINT32(save_p, numgotboxes); WRITEUINT8(save_p, numtargets); @@ -4614,8 +4609,8 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) // gamemap changed; we assume that its map header is always valid, // so make it so - if(!mapheaderinfo[gamemap-1]) - P_AllocMapHeader(gamemap-1); + if (!gamemap || gamemap > nummapheaders || !mapheaderinfo[gamemap-1]) + I_Error("P_NetUnArchiveMisc: Internal map ID %d not found (nummapheaders = %d)", gamemap-1, nummapheaders); // tell the sound code to reset the music since we're skipping what // normally sets this flag @@ -4649,7 +4644,6 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) // get the time leveltime = READUINT32(save_p); - ssspheres = READUINT32(save_p); lastmap = READINT16(save_p); bossdisabled = READUINT16(save_p); @@ -4672,7 +4666,6 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) } token = READUINT32(save_p); - sstimer = READINT32(save_p); bluescore = READUINT32(save_p); redscore = READUINT32(save_p); @@ -4701,9 +4694,6 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) gravity = READFIXED(save_p); mapobjectscale = READFIXED(save_p); - countdowntimer = (tic_t)READUINT32(save_p); - countdowntimeup = (boolean)READUINT8(save_p); - // SRB2kart numgotboxes = READINT32(save_p); numtargets = READUINT8(save_p); @@ -4880,7 +4870,7 @@ boolean P_LoadGame(INT16 mapoverride) return false; // Only do this after confirming savegame is ok - G_DeferedInitNew(false, G_BuildMapName(gamemap), savedata.skin, 0, true); + G_DeferedInitNew(false, gamemap, savedata.skin, 0, true); COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this return true; diff --git a/src/p_setup.c b/src/p_setup.c index f209d14c3..2bdbd14f9 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -134,6 +134,8 @@ boolean stoppedclock; boolean levelloading; UINT8 levelfadecol; +virtres_t *curmapvirt; + // BLOCKMAP // Created from axis aligned bounding box // of the map, a rectangular array of @@ -312,11 +314,19 @@ boolean P_IsDegeneratedTubeWaypointSequence(UINT8 sequence) FUNCNORETURN static ATTRNORETURN void CorruptMapError(const char *msg) { // don't use va() because the calling function probably uses it - char mapnum[10]; + char mapname[MAXMAPLUMPNAME]; - sprintf(mapnum, "%hd", gamemap); + if (gamemap > 0 && gamemap <= nummapheaders && mapheaderinfo[gamemap-1]) + { + sprintf(mapname, "%s", mapheaderinfo[gamemap-1]->lumpname); + } + else + { + sprintf(mapname, "ID %d", gamemap-1); + } + CON_LogMessage("Map "); - CON_LogMessage(mapnum); + CON_LogMessage(mapname); CON_LogMessage(" is corrupt: "); CON_LogMessage(msg); CON_LogMessage("\n"); @@ -357,59 +367,34 @@ void P_DeleteFlickies(INT16 i) * \param i Map number to clear header for. * \sa P_ClearMapHeaderInfo */ -static void P_ClearSingleMapHeaderInfo(INT16 i) +static void P_ClearSingleMapHeaderInfo(INT16 num) { - const INT16 num = (INT16)(i-1); - - boolean exists = (mapheaderinfo[gamemap-1]->alreadyExists == true); - mapheaderinfo[num]->lvlttl[0] = '\0'; - mapheaderinfo[num]->selectheading[0] = '\0'; mapheaderinfo[num]->subttl[0] = '\0'; mapheaderinfo[num]->zonttl[0] = '\0'; - mapheaderinfo[num]->ltzzpatch[0] = '\0'; - mapheaderinfo[num]->ltzztext[0] = '\0'; - mapheaderinfo[num]->ltactdiamond[0] = '\0'; mapheaderinfo[num]->actnum = 0; mapheaderinfo[num]->typeoflevel = 0; - mapheaderinfo[num]->nextlevel = (INT16)(i + 1); - mapheaderinfo[num]->marathonnext = 0; - mapheaderinfo[num]->startrings = 0; - mapheaderinfo[num]->sstimer = 90; - mapheaderinfo[num]->ssspheres = 1; mapheaderinfo[num]->gravity = DEFAULT_GRAVITY; mapheaderinfo[num]->keywords[0] = '\0'; - snprintf(mapheaderinfo[num]->musname, 7, "%sM", G_BuildMapName(i)); + sprintf(mapheaderinfo[num]->musname, "%.5sM", G_BuildMapName(num+1)); mapheaderinfo[num]->musname[6] = 0; mapheaderinfo[num]->mustrack = 0; mapheaderinfo[num]->muspos = 0; - mapheaderinfo[num]->musinterfadeout = 0; - mapheaderinfo[num]->musintername[0] = 0; - mapheaderinfo[num]->muspostbossname[0] = 0; - mapheaderinfo[num]->muspostbosstrack = 0; - mapheaderinfo[num]->muspostbosspos = 0; - mapheaderinfo[num]->muspostbossfadein = 0; - mapheaderinfo[num]->musforcereset = -1; - mapheaderinfo[num]->forcecharacter[0] = '\0'; mapheaderinfo[num]->weather = PRECIP_NONE; snprintf(mapheaderinfo[num]->skytexture, 5, "SKY1"); mapheaderinfo[num]->skytexture[4] = 0; mapheaderinfo[num]->skybox_scalex = 16; mapheaderinfo[num]->skybox_scaley = 16; mapheaderinfo[num]->skybox_scalez = 16; - mapheaderinfo[num]->interscreen[0] = '#'; mapheaderinfo[num]->runsoc[0] = '#'; mapheaderinfo[num]->scriptname[0] = '#'; mapheaderinfo[num]->precutscenenum = 0; mapheaderinfo[num]->cutscenenum = 0; - mapheaderinfo[num]->countdown = 0; mapheaderinfo[num]->palette = UINT16_MAX; mapheaderinfo[num]->encorepal = UINT16_MAX; mapheaderinfo[num]->numlaps = NUMLAPS_DEFAULT; mapheaderinfo[num]->unlockrequired = -1; mapheaderinfo[num]->levelselect = 0; - mapheaderinfo[num]->bonustype = 0; - mapheaderinfo[num]->maxbonuslives = -1; mapheaderinfo[num]->levelflags = 0; mapheaderinfo[num]->menuflags = 0; mapheaderinfo[num]->mobj_scale = FRACUNIT; @@ -422,10 +407,10 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) #else // equivalent to "FlickyList = NONE" P_DeleteFlickies(num); #endif - P_DeleteGrades(num); - // see p_setup.c - prevents replacing maps in addons with different versions - mapheaderinfo[num]->alreadyExists = exists; + mapheaderinfo[num]->mapvisited = 0; + Z_Free(mapheaderinfo[num]->mainrecord); + mapheaderinfo[num]->mainrecord = NULL; mapheaderinfo[num]->customopts = NULL; mapheaderinfo[num]->numCustomOptions = 0; @@ -437,110 +422,53 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) */ void P_AllocMapHeader(INT16 i) { - if (!mapheaderinfo[i]) + if (i > nummapheaders) + I_Error("P_AllocMapHeader: Called on %d, should be %d", i, nummapheaders); + + if (i >= NEXTMAP_SPECIAL) { - mapheaderinfo[i] = Z_Malloc(sizeof(mapheader_t), PU_STATIC, NULL); - mapheaderinfo[i]->flickies = NULL; - mapheaderinfo[i]->grades = NULL; - } - P_ClearSingleMapHeaderInfo(i + 1); -} - -/** NiGHTS Grades are a special structure, - * we initialize them here. - * - * \param i Index of header to allocate grades for - * \param mare The mare we're adding grades for - * \param grades the string from DeHackEd, we work with it ourselves - */ -void P_AddGradesForMare(INT16 i, UINT8 mare, char *gtext) -{ - INT32 g; - char *spos = gtext; - - CONS_Debug(DBG_SETUP, "Map %d Mare %d: ", i+1, (UINT16)mare+1); - - if (mapheaderinfo[i]->numGradedMares < mare+1) - { - mapheaderinfo[i]->numGradedMares = mare+1; - mapheaderinfo[i]->grades = Z_Realloc(mapheaderinfo[i]->grades, sizeof(nightsgrades_t) * mapheaderinfo[i]->numGradedMares, PU_STATIC, NULL); + I_Error("P_AllocMapHeader: Too many maps!"); } - for (g = 0; g < 6; ++g) + if (i >= mapallocsize) { - // Allow "partial" grading systems - if (spos != NULL) + if (!mapallocsize) { - mapheaderinfo[i]->grades[mare].grade[g] = atoi(spos); - CONS_Debug(DBG_SETUP, "%u ", atoi(spos)); - // Grab next comma - spos = strchr(spos, ','); - if (spos) - ++spos; + mapallocsize = 16; } else { - // Grade not reachable - mapheaderinfo[i]->grades[mare].grade[g] = UINT32_MAX; + mapallocsize *= 2; } + + mapheaderinfo = Z_ReallocAlign( + (void*) mapheaderinfo, + sizeof(mapheader_t*) * mapallocsize, + PU_STATIC, + NULL, + sizeof(mapheader_t*) * 8 + ); + + if (!mapheaderinfo) + I_Error("P_AllocMapHeader: Not enough memory to realloc mapheaderinfo (size %d)", mapallocsize); } - CONS_Debug(DBG_SETUP, "\n"); -} - -/** And this removes the grades safely. - * - * \param i The header to remove grades from - */ -void P_DeleteGrades(INT16 i) -{ - if (mapheaderinfo[i]->grades) - Z_Free(mapheaderinfo[i]->grades); - - mapheaderinfo[i]->grades = NULL; - mapheaderinfo[i]->numGradedMares = 0; -} - -/** And this fetches the grades - * - * \param pscore The player's score. - * \param map The game map. - * \param mare The mare to test. - */ -UINT8 P_GetGrade(UINT32 pscore, INT16 map, UINT8 mare) -{ - INT32 i; - - // Determining the grade - if (mapheaderinfo[map-1] && mapheaderinfo[map-1]->grades && mapheaderinfo[map-1]->numGradedMares >= mare + 1) + if (!mapheaderinfo[i]) { - INT32 pgrade = 0; - for (i = 0; i < 6; ++i) - { - if (pscore >= mapheaderinfo[map-1]->grades[mare].grade[i]) - ++pgrade; - } - return (UINT8)pgrade; + mapheaderinfo[i] = Z_Malloc(sizeof(mapheader_t), PU_STATIC, NULL); + if (!mapheaderinfo[i]) + I_Error("P_AllocMapHeader: Not enough memory to allocate new mapheader (ID %d)", i); + + mapheaderinfo[i]->lumpnum = LUMPERROR; + mapheaderinfo[i]->lumpname = NULL; + mapheaderinfo[i]->thumbnailPic = NULL; + mapheaderinfo[i]->minimapPic = NULL; + mapheaderinfo[i]->cup = NULL; + mapheaderinfo[i]->mainrecord = NULL; + mapheaderinfo[i]->flickies = NULL; + nummapheaders++; } - return 0; -} - -UINT8 P_HasGrades(INT16 map, UINT8 mare) -{ - // Determining the grade - // Mare 0 is treated as overall and is true if ANY grades exist - if (mapheaderinfo[map-1] && mapheaderinfo[map-1]->grades - && (mare == 0 || mapheaderinfo[map-1]->numGradedMares >= mare)) - return true; - return false; -} - -UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade) -{ - // Get the score for the grade... if it exists - if (grade == GRADE_F || grade > GRADE_S || !P_HasGrades(map, mare)) return 0; - - return mapheaderinfo[map-1]->grades[mare].grade[grade-1]; + P_ClearSingleMapHeaderInfo(i); } // @@ -736,69 +664,6 @@ void P_ReloadRings(void) } } -#ifdef SCANTHINGS -void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum) -{ - size_t i, n; - UINT8 *data, *datastart; - UINT16 type, maprings; - INT16 tol; - UINT32 flags; - - tol = mapheaderinfo[mapnum-1]->typeoflevel; - flags = mapheaderinfo[mapnum-1]->levelflags; - - n = W_LumpLengthPwad(wadnum, lumpnum) / (5 * sizeof (INT16)); - //CONS_Printf("%u map things found!\n", n); - - maprings = 0; - data = datastart = W_CacheLumpNumPwad(wadnum, lumpnum, PU_STATIC); - for (i = 0; i < n; i++) - { - data += 3 * sizeof (INT16); // skip x y position, angle - type = READUINT16(data) & 4095; - data += sizeof (INT16); // skip options - - if (mt->type == mobjinfo[MT_RANDOMITEM].doomednum) - { - nummapboxes++; - } - else if (mt->type == mobjinfo[MT_BATTLECAPSULE].doomednum) - { - maptargets++; - } - else if (mt->type == mobjinfo[MT_RING].doomednum) - { - maprings++; - } - else - { - switch (type) - { - case 603: // 10 diagonal rings - maprings += 10; - break; - case 600: // 5 vertical rings - case 601: // 5 vertical rings - case 602: // 5 diagonal rings - maprings += 5; - break; - case 604: // 8 circle rings - maprings += 8; - break; - case 605: // 16 circle rings - maprings += 16; - break; - } - } - } - Z_Free(datastart); - - if (maprings) - CONS_Printf("%s has %u rings\n", G_BuildMapName(mapnum), maprings); -} -#endif - static void P_SpawnMapThings(boolean spawnemblems) { size_t i; @@ -847,6 +712,7 @@ static void P_SpawnMapThings(boolean spawnemblems) // Experimental groovy write function! void P_WriteThings(void) { + const char * filename; size_t i, length; mapthing_t *mt; UINT8 *savebuffer, *savebuf_p; @@ -875,11 +741,13 @@ void P_WriteThings(void) length = savebuf_p - savebuffer; - FIL_WriteFile(va("newthings%d.lmp", gamemap), savebuffer, length); + filename = va("newthings-%s.lmp", G_BuildMapName(gamemap)); + + FIL_WriteFile(filename, savebuffer, length); free(savebuffer); savebuf_p = NULL; - CONS_Printf(M_GetText("newthings%d.lmp saved.\n"), gamemap); + CONS_Printf(M_GetText("%s saved.\n"), filename); } // @@ -3560,15 +3428,14 @@ static void P_MakeMapMD5(virtres_t *virt, void *dest) static boolean P_LoadMapFromFile(void) { - virtres_t *virt = vres_GetMap(lastloadedmaplumpnum); - virtlump_t *textmap = vres_Find(virt, "TEXTMAP"); + virtlump_t *textmap = vres_Find(curmapvirt, "TEXTMAP"); size_t i; udmf = textmap != NULL; - if (!P_LoadMapData(virt)) + if (!P_LoadMapData(curmapvirt)) return false; - P_LoadMapBSP(virt); - P_LoadMapLUT(virt); + P_LoadMapBSP(curmapvirt); + P_LoadMapLUT(curmapvirt); P_LinkMapData(); @@ -3593,9 +3460,9 @@ static boolean P_LoadMapFromFile(void) if (sectors[i].tags.count) spawnsectors[i].tags.tags = memcpy(Z_Malloc(sectors[i].tags.count*sizeof(mtag_t), PU_LEVEL, NULL), sectors[i].tags.tags, sectors[i].tags.count*sizeof(mtag_t)); - P_MakeMapMD5(virt, &mapmd5); + P_MakeMapMD5(curmapvirt, &mapmd5); - vres_Free(virt); + vres_Free(curmapvirt); return true; } @@ -3659,15 +3526,6 @@ static void P_InitLevelSettings(void) // emerald hunt hunt1 = hunt2 = hunt3 = NULL; - // map time limit - if (mapheaderinfo[gamemap-1]->countdown) - { - countdowntimer = mapheaderinfo[gamemap-1]->countdown * TICRATE; - } - else - countdowntimer = 0; - countdowntimeup = false; - // clear ctf pointers redflag = blueflag = NULL; rflagpoint = bflagpoint = NULL; @@ -3675,7 +3533,7 @@ static void P_InitLevelSettings(void) // circuit, race and competition stuff circuitmap = false; numstarposts = 0; - ssspheres = timeinmap = 0; + timeinmap = 0; // special stage stagefailed = true; // assume failed unless proven otherwise - P_GiveEmerald or emerald touchspecial @@ -3819,43 +3677,6 @@ static void P_RunLevelScript(const char *scriptname) COM_BufExecute(); // Run it! } -static void P_ForceCharacter(const char *forcecharskin) -{ - UINT8 i; - - if (netgame) - { - char skincmd[33]; - - for (i = 0; i <= splitscreen; i++) - { - const char *num = ""; - - if (i > 0) - num = va("%d", i+1); - - sprintf(skincmd, "skin%s %s\n", num, forcecharskin); - CV_Set(&cv_skin[i], forcecharskin); - } - - COM_BufAddText(skincmd); - } - else - { - for (i = 0; i <= splitscreen; i++) - { - SetPlayerSkin(g_localplayers[i], forcecharskin); - - // normal player colors in single player - if ((unsigned)cv_playercolor[i].value != skins[players[g_localplayers[i]].skin].prefcolor && !modeattacking) - { - CV_StealthSetValue(&cv_playercolor[i], skins[players[g_localplayers[i]].skin].prefcolor); - players[g_localplayers[i]].skincolor = skins[players[g_localplayers[i]].skin].prefcolor; - } - } - } -} - static void P_ResetSpawnpoints(void) { UINT8 i; @@ -3939,17 +3760,20 @@ static void P_LoadRecordGhosts(void) if (cv_ghost_guest.value && FIL_FileExists(va("%s-guest.lmp", gpath))) G_AddGhost(va("%s-guest.lmp", gpath)); +#ifdef STAFFGHOSTS // Staff Attack ghosts if (cv_ghost_staff.value) { lumpnum_t l; UINT8 j = 1; - while (j <= 99 && (l = W_CheckNumForName(va("%sS%02u",G_BuildMapName(gamemap),j))) != LUMPERROR) + // TODO: Use map header to determine lump name + while (j <= 99 && (l = W_CheckNumForLongName(va("%sS%02u",G_BuildMapName(gamemap),j))) != LUMPERROR) { G_AddGhost(va("%sS%02u",G_BuildMapName(gamemap),j)); j++; } } +#endif //#ifdef STAFFGHOSTS free(gpath); } @@ -4096,6 +3920,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // Map header should always be in place at this point INT32 i, ranspecialwipe = 0; sector_t *ss; + virtlump_t *encoreLump = NULL; levelloading = true; @@ -4133,9 +3958,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) for (i = 0; i <= r_splitscreen; i++) postimgtype[i] = postimg_none; - if (mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0') - P_ForceCharacter(mapheaderinfo[gamemap-1]->forcecharacter); - // Initial height of PointOfView // will be set by player think. players[consoleplayer].viewz = 1; @@ -4317,13 +4139,33 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) } // internal game map - maplumpname = G_BuildMapName(gamemap); - lastloadedmaplumpnum = W_CheckNumForMap(maplumpname); + maplumpname = mapheaderinfo[gamemap-1]->lumpname; + lastloadedmaplumpnum = mapheaderinfo[gamemap-1]->lumpnum; if (lastloadedmaplumpnum == LUMPERROR) I_Error("Map %s not found.\n", maplumpname); - R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette, - W_CheckNumForName(va("%s%c", maplumpname, (encoremode ? 'E' : 'T')))); + curmapvirt = vres_GetMap(lastloadedmaplumpnum); + + if (mapheaderinfo[gamemap-1]) + { + if (encoremode) + { + encoreLump = vres_Find(curmapvirt, "ENCORE"); + } + else + { + encoreLump = vres_Find(curmapvirt, "TWEAKMAP"); + } + } + + if (encoreLump) + { + R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette, encoreLump->data, encoreLump->size); + } + else + { + R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette, NULL, 0); + } CON_SetupBackColormap(); // SRB2 determines the sky texture to be used depending on the map header. @@ -4424,9 +4266,9 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) skipstats = 0; if (!(netgame || multiplayer || demo.playback) && !majormods) - mapvisited[gamemap-1] |= MV_VISITED; + mapheaderinfo[gamemap-1]->mapvisited |= MV_VISITED; else if (!demo.playback) - mapvisited[gamemap-1] |= MV_MP; // you want to record that you've been there this session, but not permanently + mapheaderinfo[gamemap-1]->mapvisited |= MV_MP; // you want to record that you've been there this session, but not permanently G_AddMapToBuffer(gamemap-1); @@ -4613,6 +4455,118 @@ static lumpinfo_t* FindFolder(const char *folName, UINT16 *start, UINT16 *end, l return lumpinfo; } +// Initialising map data (and catching replacements)... +UINT8 P_InitMapData(INT32 numexistingmapheaders) +{ + UINT8 ret = 0; + INT32 i; + lumpnum_t maplump; + virtres_t *virtmap; + virtlump_t *minimap, *thumbnailPic; + char *name; + + for (i = 0; i < nummapheaders; ++i) + { + name = mapheaderinfo[i]->lumpname; + maplump = W_CheckNumForMap(name); + + // Doesn't exist? + if (maplump == INT16_MAX) + { +#ifndef DEVELOP + if (!numexistingmapheaders) + { + I_Error("P_InitMapData: Base map %s has a header but no level\n", name); + } +#endif + continue; + } + + // No change? + if (mapheaderinfo[i]->lumpnum == maplump) + continue; + + // Okay, it does... + { + ret |= MAPRET_ADDED; + CONS_Printf("%s\n", name); + + if (numexistingmapheaders && mapheaderinfo[i]->lumpnum != LUMPERROR) + { + G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you + + //If you replaced the map you're on, end the level when done. + if (i == gamemap - 1) + ret |= MAPRET_CURRENTREPLACED; + } + + mapheaderinfo[i]->lumpnum = maplump; + + // Get map thumbnail and minimap + virtmap = vres_GetMap(mapheaderinfo[i]->lumpnum); + thumbnailPic = vres_Find(virtmap, "PICTURE"); + minimap = vres_Find(virtmap, "MINIMAP"); + + // Clear out existing graphics... + if (mapheaderinfo[i]->thumbnailPic) + { + Patch_Free(mapheaderinfo[i]->thumbnailPic); + } + + if (mapheaderinfo[i]->minimapPic) + { + Patch_Free(mapheaderinfo[i]->minimapPic); + } + + // Now apply the new ones! + if (thumbnailPic) + { + mapheaderinfo[i]->thumbnailPic = vres_GetPatch(thumbnailPic, PU_STATIC); + } + + if (minimap) + { + mapheaderinfo[i]->minimapPic = vres_GetPatch(minimap, PU_STATIC); + } + + vres_Free(virtmap); + + // Now associate it with a cup cache. + // (The core assumption is that cups < headers.) + if (i >= numexistingmapheaders) + { + cupheader_t *cup = kartcupheaders; + INT32 j; + while (cup) + { + for (j = 0; j < CUPCACHE_MAX; j++) + { + // Already discovered? + if (cup->cachedlevels[j] != NEXTMAP_INVALID) + continue; + + if (!cup->levellist[j] || strcasecmp(cup->levellist[j], name) != 0) + continue; + + // Only panic about back-reference for non-bonus material. + if (j < MAXLEVELLIST || j == CUPCACHE_SPECIAL) + { + if (mapheaderinfo[i]->cup) + I_Error("P_InitMapData: Map %s cannot appear in cups multiple times! (First in %s, now in %s)", name, mapheaderinfo[i]->cup->name, cup->name); + mapheaderinfo[i]->cup = cup; + } + + cup->cachedlevels[j] = i; + } + cup = cup->next; + } + } + } + } + + return ret; +} + UINT16 p_adding_file = INT16_MAX; // @@ -4622,13 +4576,13 @@ UINT16 p_adding_file = INT16_MAX; boolean P_AddWadFile(const char *wadfilename) { size_t i, j, sreplaces = 0, mreplaces = 0, digmreplaces = 0; + INT32 numexistingmapheaders = nummapheaders; UINT16 numlumps, wadnum; char *name; lumpinfo_t *lumpinfo; //boolean texturechange = false; ///\todo Useless; broken when back-frontporting PK3 changes? - boolean mapsadded = false; - boolean replacedcurrentmap = false; + UINT8 mapsadded = 0; // Vars to help us with the position start and amount of each resource type. // Useful for PK3s since they use folders. @@ -4778,36 +4732,8 @@ boolean P_AddWadFile(const char *wadfilename) // // search for maps // - lumpinfo = wadfiles[wadnum]->lumpinfo; - for (i = 0; i < numlumps; i++, lumpinfo++) - { - name = lumpinfo->name; - if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers - { - INT16 num; - if (name[5]!='\0') - continue; - num = (INT16)M_MapNumber(name[3], name[4]); + mapsadded = P_InitMapData(numexistingmapheaders); - // we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant - if (num <= NUMMAPS && mapheaderinfo[num-1]) - { - if (mapheaderinfo[num - 1]->alreadyExists != false) - { - G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you - } - - mapheaderinfo[num - 1]->alreadyExists = true; - } - - //If you replaced the map you're on, end the level when done. - if (num == gamemap) - replacedcurrentmap = true; - - CONS_Printf("%s\n", name); - mapsadded = true; - } - } if (!mapsadded) CONS_Printf(M_GetText("No maps added\n")); @@ -4825,7 +4751,7 @@ boolean P_AddWadFile(const char *wadfilename) if (cursaveslot > 0) cursaveslot = 0; - if (replacedcurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer)) + if ((mapsadded & MAPRET_CURRENTREPLACED) && gamestate == GS_LEVEL && (netgame || multiplayer)) { CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap); if (server) diff --git a/src/p_setup.h b/src/p_setup.h index 7a8a8c18f..3db26a7ba 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -99,15 +99,17 @@ extern mapthing_t *mapthings; extern UINT16 p_adding_file; void P_SetupLevelSky(const char *skytexname, boolean global); -#ifdef SCANTHINGS -void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum); -#endif void P_RespawnThings(void); boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate); #ifdef HWRENDER void HWR_LoadLevel(void); #endif boolean P_AddWadFile(const char *wadfilename); + +#define MAPRET_ADDED (1) +#define MAPRET_CURRENTREPLACED (1<<1) +UINT8 P_InitMapData(INT32 numexistingmapheaders); + boolean P_RunSOC(const char *socfilename); void P_LoadSoundsRange(UINT16 wadnum, UINT16 first, UINT16 num); void P_LoadMusicsRange(UINT16 wadnum, UINT16 first, UINT16 num); @@ -123,10 +125,5 @@ void P_DeleteFlickies(INT16 i); // Needed for NiGHTS void P_ReloadRings(void); -void P_DeleteGrades(INT16 i); -void P_AddGradesForMare(INT16 i, UINT8 mare, char *gtext); -UINT8 P_GetGrade(UINT32 pscore, INT16 map, UINT8 mare); -UINT8 P_HasGrades(INT16 map, UINT8 mare); -UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade); #endif diff --git a/src/p_spec.c b/src/p_spec.c index 3e8edb2b4..d24e3fbe8 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2522,6 +2522,9 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) break; case 415: // Run a script + // FIXME: cursed + CONS_Alert(CONS_WARNING, "Linedef special 415 is currently broken! Fix it later, BYE.\n"); +#if 0 if (cv_runscripts.value) { INT32 scrnum; @@ -2556,6 +2559,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) else COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE)); } +#endif break; case 416: // Spawn adjustable fire flicker @@ -5927,10 +5931,6 @@ void P_InitSpecials(void) maplighting.directional = mapheaderinfo[gamemap-1]->use_light_angle; maplighting.angle = mapheaderinfo[gamemap-1]->light_angle; - // Defaults in case levels don't have them set. - sstimer = mapheaderinfo[gamemap-1]->sstimer*TICRATE + 6; - ssspheres = mapheaderinfo[gamemap-1]->ssspheres; - CheckForBustableBlocks = CheckForBouncySector = CheckForQuicksand = CheckForMarioBlocks = CheckForFloatBob = CheckForReverseGravity = false; // Set weather @@ -6022,11 +6022,6 @@ void P_SpawnSpecials(boolean fromnetsave) // Process Section 2 switch(GETSECSPECIAL(sector->special, 2)) { - case 10: // Time for special stage - sstimer = (sector->floorheight>>FRACBITS) * TICRATE + 6; // Time to finish - ssspheres = sector->ceilingheight>>FRACBITS; // Ring count for special stage - break; - case 11: // Custom global gravity! gravity = sector->floorheight/1000; break; diff --git a/src/p_user.c b/src/p_user.c index e74a3e986..6ac150a6a 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -300,11 +300,15 @@ boolean P_PlayerMoving(INT32 pnum) // UINT8 P_GetNextEmerald(void) { - if (gamemap >= sstage_start && gamemap <= sstage_end) - return (UINT8)(gamemap - sstage_start); - if (gamemap >= smpstage_start || gamemap <= smpstage_end) - return (UINT8)(gamemap - smpstage_start); - return 0; + INT16 mapnum = gamemap-1; + + if (mapnum > nummapheaders || !mapheaderinfo[mapnum]) + return 0; + + if (!mapheaderinfo[mapnum]->cup || mapheaderinfo[mapnum]->cup->cachedlevels[CUPCACHE_SPECIAL] != mapnum) + return 0; + + return mapheaderinfo[mapnum]->cup->emeraldnum; } // @@ -2144,9 +2148,6 @@ void P_MovePlayer(player_t *player) fixed_t runspd; - if (countdowntimeup) - return; - cmd = &player->cmd; runspd = 14*player->mo->scale; //srb2kart @@ -4288,14 +4289,7 @@ void P_PlayerThink(player_t *player) if (!player->spectator) P_PlayerInSpecialSector(player); - else if ( -#else - if (player->spectator && #endif - (gametyperules & GTR_LIVES)) - { - /*P_ConsiderAllGone()*/; - } if (player->playerstate == PST_DEAD) { diff --git a/src/r_data.c b/src/r_data.c index ddeb5e3b5..353a9bbf5 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -286,7 +286,7 @@ void R_InitColormaps(void) #endif } -void R_ReInitColormaps(UINT16 num, lumpnum_t newencoremap) +void R_ReInitColormaps(UINT16 num, void *newencoremap, size_t encoremapsize) { char colormap[9] = "COLORMAP"; lumpnum_t lump; @@ -309,13 +309,13 @@ void R_ReInitColormaps(UINT16 num, lumpnum_t newencoremap) W_ReadLumpHeader(lump, colormaps, W_LumpLength(basecolormaplump), 0U); // Encore mode. - if (newencoremap != LUMPERROR) + if (newencoremap) { lighttable_t *colormap_p, *colormap_p2; size_t p, i; encoremap = Z_MallocAlign(256 + 10, PU_LEVEL, NULL, 8); - W_ReadLump(newencoremap, encoremap); + M_Memcpy(encoremap, newencoremap, encoremapsize); colormap_p = colormap_p2 = colormaps; colormap_p += COLORMAP_REMAPOFFSET; diff --git a/src/r_data.h b/src/r_data.h index 3c8908a59..da3f81163 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -55,7 +55,7 @@ extern size_t flatmemory, spritememory, texturememory; //#define COLORMAPREVERSELIST void R_InitColormaps(void); -void R_ReInitColormaps(UINT16 num, lumpnum_t newencoremap); +void R_ReInitColormaps(UINT16 num, void *newencoremap, size_t encoremapsize); void R_ClearColormaps(void); extracolormap_t *R_CreateDefaultColormap(boolean lighttable); extracolormap_t *R_GetDefaultColormap(void); diff --git a/src/r_defs.h b/src/r_defs.h index 8be91fc9b..ca2f5152d 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -698,6 +698,7 @@ typedef struct } patch_t; extern patch_t *missingpat; +extern patch_t *blanklvl; #if defined(_MSC_VER) #pragma pack(1) diff --git a/src/r_patch.c b/src/r_patch.c index 544c15ae8..8cf89fa3d 100644 --- a/src/r_patch.c +++ b/src/r_patch.c @@ -101,7 +101,7 @@ static void Patch_FreeData(patch_t *patch) void Patch_Free(patch_t *patch) { - if (patch == missingpat) + if (!patch || patch == missingpat) return; Patch_FreeData(patch); Z_Free(patch); diff --git a/src/r_skins.c b/src/r_skins.c index ae5e0363c..f8410c15f 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -191,12 +191,6 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum) return true; } - if (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) - { - // Being forced to play as this character by the level - return true; - } - if (netgame && (cv_forceskin.value == skinnum)) { // Being forced to play as this character by the server diff --git a/src/s_sound.c b/src/s_sound.c index 12e35e5c5..674581540 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -386,7 +386,6 @@ void S_StopSoundByID(void *origin, sfxenum_t sfx_id) if (channels[cnum].sfxinfo == &S_sfx[sfx_id] && channels[cnum].origin == origin) { S_StopChannel(cnum); - break; } } } @@ -407,7 +406,6 @@ void S_StopSoundByNum(sfxenum_t sfxnum) if (channels[cnum].sfxinfo == &S_sfx[sfxnum]) { S_StopChannel(cnum); - break; } } } @@ -763,7 +761,6 @@ void S_StopSound(void *origin) if (channels[cnum].sfxinfo && channels[cnum].origin == origin) { S_StopChannel(cnum); - break; } } } @@ -2394,6 +2391,7 @@ void S_StartEx(boolean reset) music_stack_fadein = JINGLEPOSTFADE; } +// TODO: fix this function, needs better support for map names static void Command_Tunes_f(void) { const char *tunearg; diff --git a/src/v_video.c b/src/v_video.c index c2d3e3c4c..8ed5ed53f 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -404,7 +404,7 @@ const char *GetPalette(void) return "PLAYPAL"; } -static void LoadMapPalette(void) +void V_ReloadPalette(void) { LoadPalette(GetPalette()); } @@ -416,7 +416,7 @@ static void LoadMapPalette(void) void V_SetPalette(INT32 palettenum) { if (!pLocalPalette) - LoadMapPalette(); + V_ReloadPalette(); #ifdef HWRENDER if (rendermode == render_opengl) @@ -449,7 +449,7 @@ void V_SetPaletteLump(const char *pal) static void CV_palette_OnChange(void) { // reload palette - LoadMapPalette(); + V_ReloadPalette(); V_SetPalette(0); } @@ -2980,8 +2980,6 @@ void V_Init(void) UINT8 *base = vid.buffer; const INT32 screensize = vid.rowbytes * vid.height; - LoadMapPalette(); - for (i = 0; i < NUMSCREENS; i++) screens[i] = NULL; diff --git a/src/v_video.h b/src/v_video.h index 099d22d86..9564bca15 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -56,6 +56,9 @@ void InitColorLUT(colorlookup_t *lut, RGBA_t *palette, boolean makecolors); UINT8 GetColorLUT(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b); UINT8 GetColorLUTDirect(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b); +// Loads the correct palette into memory +void V_ReloadPalette(void); + // Set the current RGB palette lookup to use for palettized graphics void V_SetPalette(INT32 palettenum); diff --git a/src/w_wad.c b/src/w_wad.c index a2efb785d..099ce0ded 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -64,10 +64,6 @@ #include "i_system.h" #include "md5.h" #include "lua_script.h" -#ifdef SCANTHINGS -#include "p_setup.h" // P_ScanThings -#endif -#include "m_misc.h" // M_MapNumber #include "g_game.h" // G_SetGameModified #include "k_terrain.h" @@ -94,10 +90,12 @@ typedef struct // Must be a power of two #define LUMPNUMCACHESIZE 64 +#define LUMPNUMCACHENAME 32 typedef struct lumpnum_cache_s { - char lumpname[32]; + char lumpname[LUMPNUMCACHENAME]; + UINT32 lumphash; lumpnum_t lumpnum; } lumpnum_cache_t; @@ -273,22 +271,6 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile) DEH_LoadDehackedLumpPwad(wadnum, lump, mainfile); } } - -#ifdef SCANTHINGS - // Scan maps for emblems 'n shit - { - lumpinfo_t *lump_p = wadfiles[wadnum]->lumpinfo; - for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++) - { - const char *name = lump_p->name; - if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P' && name[5]=='\0') - { - INT16 mapnum = (INT16)M_MapNumber(name[3], name[4]); - P_ScanThings(mapnum, wadnum, lump + ML_THINGS); - } - } - } -#endif } /** Compute MD5 message digest for bytes read from STREAM of this filname. @@ -975,6 +957,53 @@ UINT16 W_FindNextEmptyInPwad(UINT16 wad, UINT16 startlump) return INT16_MAX; } +// Get a map marker for WADs, and a standalone WAD file lump inside PK3s. +UINT16 W_CheckNumForMapPwad(const char *name, UINT16 wad, UINT16 startlump) +{ + UINT16 i, end; + + if (wadfiles[wad]->type == RET_WAD) + { + for (i = startlump; i < wadfiles[wad]->numlumps; i++) + { + // Not the name? + if (strcasecmp(name, (wadfiles[wad]->lumpinfo + i)->name)) + continue; + + // Not a header? + if (W_LumpLength(i | (wad << 16)) > 0) + continue; + + return i; + } + } + else if (wadfiles[wad]->type == RET_PK3) + { + i = W_CheckNumForFolderStartPK3("maps/", wad, startlump); + + if (i != INT16_MAX) + { + end = W_CheckNumForFolderEndPK3("maps/", wad, i); + + // Now look for the specified map. + for (; i < end; i++) + { + // Not the name? + if (strcasecmp(name, (wadfiles[wad]->lumpinfo + i)->longname)) + continue; + + // Not a .wad? + if (!W_IsLumpWad(i | (wad << 16))) + continue; + + return i; + } + } + } + + return INT16_MAX; +} + // // Same as the original, but checks in one pwad only. // wadid is a wad number @@ -1114,8 +1143,12 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump) // lumpnum_t W_CheckNumForName(const char *name) { - INT32 i; lumpnum_t check = INT16_MAX; + UINT32 hash = quickncasehash(name, 8); + INT32 i; + + if (name == NULL) + return LUMPERROR; if (!*name) // some doofus gave us an empty string? return LUMPERROR; @@ -1125,6 +1158,7 @@ lumpnum_t W_CheckNumForName(const char *name) for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--) { if (!lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname[8] + && lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumphash == hash && strncmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name, 8) == 0) { lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1); @@ -1140,14 +1174,18 @@ lumpnum_t W_CheckNumForName(const char *name) break; //found it } - if (check == INT16_MAX) return LUMPERROR; + if (check == INT16_MAX) + { + return LUMPERROR; + } else { // Update the cache. lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1); - memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', 32); + memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', LUMPNUMCACHENAME); strncpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 8); - lumpnumcache[lumpnumcacheindex].lumpnum = (i<<16)+check; + lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check; + lumpnumcache[lumpnumcacheindex].lumphash = hash; return lumpnumcache[lumpnumcacheindex].lumpnum; } @@ -1161,8 +1199,12 @@ lumpnum_t W_CheckNumForName(const char *name) // lumpnum_t W_CheckNumForLongName(const char *name) { - INT32 i; lumpnum_t check = INT16_MAX; + UINT32 hash = quickncasehash(name, LUMPNUMCACHENAME); + INT32 i; + + if (name == NULL) + return LUMPERROR; if (!*name) // some doofus gave us an empty string? return LUMPERROR; @@ -1171,7 +1213,8 @@ lumpnum_t W_CheckNumForLongName(const char *name) // most recent entries first for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--) { - if (strcmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name) == 0) + if (lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumphash == hash + && strcmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name) == 0) { lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1); return lumpnumcache[lumpnumcacheindex].lumpnum; @@ -1186,16 +1229,20 @@ lumpnum_t W_CheckNumForLongName(const char *name) break; //found it } - if (check == INT16_MAX) return LUMPERROR; + if (check == INT16_MAX) + { + return LUMPERROR; + } else { - if (strlen(name) < 32) + if (strlen(name) < LUMPNUMCACHENAME) { // Update the cache. lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1); - memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', 32); - strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 32); + memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', LUMPNUMCACHENAME); + strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, LUMPNUMCACHENAME); lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check; + lumpnumcache[lumpnumcacheindex].lumphash = hash; } return (i << 16) + check; @@ -1204,45 +1251,50 @@ lumpnum_t W_CheckNumForLongName(const char *name) // Look for valid map data through all added files in descendant order. // Get a map marker for WADs, and a standalone WAD file lump inside PK3s. -// TODO: Make it search through cache first, maybe...? lumpnum_t W_CheckNumForMap(const char *name) { - UINT32 hash = quickncasehash(name, 8); - UINT16 lumpNum, end; - UINT32 i; - lumpinfo_t *p; - for (i = numwadfiles - 1; i < numwadfiles; i--) + lumpnum_t check = INT16_MAX; + UINT32 hash = quickncasehash(name, LUMPNUMCACHENAME); + INT32 i; + + // Check the lumpnumcache first. Loop backwards so that we check + // most recent entries first + for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--) { - if (wadfiles[i]->type == RET_WAD) + if (lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumphash == hash + && strcasecmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name) == 0) { - for (lumpNum = 0; lumpNum < wadfiles[i]->numlumps; lumpNum++) - { - p = wadfiles[i]->lumpinfo + lumpNum; - if (p->hash == hash && !strncmp(name, p->name, 8)) - return (i<<16) + lumpNum; - } - } - else if (wadfiles[i]->type == RET_PK3) - { - lumpNum = W_CheckNumForFolderStartPK3("maps/", i, 0); - if (lumpNum != INT16_MAX) - end = W_CheckNumForFolderEndPK3("maps/", i, lumpNum); - else - continue; - // Now look for the specified map. - for (; lumpNum < end; lumpNum++) - { - p = wadfiles[i]->lumpinfo + lumpNum; - if (p->hash == hash && !strnicmp(name, p->name, 8)) - { - const char *extension = strrchr(p->fullname, '.'); - if (!(extension && stricmp(extension, ".wad"))) - return (i<<16) + lumpNum; - } - } + lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1); + return lumpnumcache[lumpnumcacheindex].lumpnum; } } - return LUMPERROR; + + for (i = numwadfiles - 1; i >= 0; i--) + { + check = W_CheckNumForMapPwad(name, (UINT16)i, 0); + + if (check != INT16_MAX) + break; // found it + } + + if (check == INT16_MAX) + { + return LUMPERROR; + } + else + { + if (strlen(name) < LUMPNUMCACHENAME) + { + // Update the cache. + lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1); + memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', LUMPNUMCACHENAME); + strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, LUMPNUMCACHENAME); + lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check; + lumpnumcache[lumpnumcacheindex].lumphash = hash; + } + + return (i << 16) + check; + } } // @@ -1714,6 +1766,27 @@ void *W_CacheLumpName(const char *name, INT32 tag) // Cache a patch into heap memory, convert the patch format as necessary // +static void *MakePatch(void *lumpdata, size_t size, INT32 tag, void *cache) +{ + void *ptr, *dest; + size_t len = size; + + ptr = lumpdata; + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)lumpdata, len)) + { + ptr = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, len, &len, 0); + } +#endif + + dest = Z_Calloc(sizeof(patch_t), tag, cache); + + Patch_Create(ptr, len, dest); + + return dest; +} + void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) { lumpcache_t *lumpcache = NULL; @@ -1726,21 +1799,13 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) if (!lumpcache[lump]) { size_t len = W_LumpLengthPwad(wad, lump); - void *ptr, *dest, *lumpdata = Z_Malloc(len, PU_STATIC, NULL); + void *lumpdata = Z_Malloc(len, PU_STATIC, NULL); // read the lump in full W_ReadLumpHeaderPwad(wad, lump, lumpdata, 0, 0); - ptr = lumpdata; -#ifndef NO_PNG_LUMPS - if (Picture_IsLumpPNG((UINT8 *)lumpdata, len)) - ptr = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, len, &len, 0); -#endif - - dest = Z_Calloc(sizeof(patch_t), tag, &lumpcache[lump]); - Patch_Create(ptr, len, dest); - - Z_Free(ptr); + MakePatch(lumpdata, len, tag, &lumpcache[lump]); + Z_Free(lumpdata); } else Z_ChangeTag(lumpcache[lump], tag); @@ -2206,32 +2271,56 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) if (W_IsLumpWad(lumpnum)) { + UINT32 realentry; + size_t *vsizecache; + // Remember that we're assuming that the WAD will have a specific set of lumps in a specific order. UINT8 *wadData = W_CacheLumpNum(lumpnum, PU_LEVEL); filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs); - numlumps = ((wadinfo_t *)wadData)->numlumps; - vlumps = Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL); - // Build the lumps. - for (i = 0; i < numlumps; i++) + i = ((wadinfo_t *)wadData)->numlumps; + vsizecache = Z_Malloc(sizeof(size_t)*i, PU_LEVEL, NULL); + + for (realentry = 0; realentry < i; realentry++) { - vlumps[i].size = (size_t)(((filelump_t *)(fileinfo + i))->size); - // Play it safe with the name in this case. - memcpy(vlumps[i].name, (fileinfo + i)->name, 8); - vlumps[i].name[8] = '\0'; - vlumps[i].data = Z_Malloc(vlumps[i].size, PU_LEVEL, NULL); // This is memory inefficient, sorry about that. - memcpy(vlumps[i].data, wadData + (fileinfo + i)->filepos, vlumps[i].size); + vsizecache[realentry] = (size_t)(((filelump_t *)(fileinfo + realentry))->size); + + if (!vsizecache[realentry]) + continue; + + numlumps++; } + vlumps = Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL); + + // Build the lumps, skipping over empty entries. + for (i = 0, realentry = 0; i < numlumps; realentry++) + { + if (vsizecache[realentry] == 0) + continue; + vlumps[i].size = vsizecache[realentry]; + // Play it safe with the name in this case. + memcpy(vlumps[i].name, (fileinfo + realentry)->name, 8); + vlumps[i].name[8] = '\0'; + vlumps[i].data = Z_Malloc(vlumps[i].size, PU_LEVEL, NULL); // This is memory inefficient, sorry about that. + memcpy(vlumps[i].data, wadData + (fileinfo + realentry)->filepos, vlumps[i].size); + i++; + } + + Z_Free(vsizecache); Z_Free(wadData); } else { - // Count number of lumps until the end of resource OR up until next "MAPXX" lump. + // Count number of lumps until the end of resource OR up until next 0-length lump. lumpnum_t lumppos = lumpnum + 1; for (i = LUMPNUM(lumppos); i < wadfiles[WADFILENUM(lumpnum)]->numlumps; i++, lumppos++, numlumps++) - if (memcmp(W_CheckNameForNum(lumppos), "MAP", 3) == 0) - break; + { + if (W_LumpLength(lumppos) > 0) + continue; + + break; + } numlumps++; vlumps = Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL); @@ -2257,7 +2346,12 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) void vres_Free(virtres_t* vres) { while (vres->numlumps--) - Z_Free(vres->vlumps[vres->numlumps].data); + { + if (vres->vlumps[vres->numlumps].data) + { + Z_Free(vres->vlumps[vres->numlumps].data); + } + } Z_Free(vres->vlumps); Z_Free(vres); } @@ -2288,3 +2382,30 @@ virtlump_t* vres_Find(const virtres_t* vres, const char* name) return &vres->vlumps[i]; return NULL; } + +/** \brief Gets patch from given virtual lump + * + * \param Virtual lump + * \return Patch data + * + */ +void *vres_GetPatch(virtlump_t *vlump, INT32 tag) +{ + patch_t *patch; + + if (!vlump) + return NULL; + + patch = MakePatch(vlump->data, vlump->size, tag, NULL); + +#ifdef HWRENDER + // Software-only compile cache the data without conversion + if (rendermode == render_soft || rendermode == render_none) +#endif + return (void *)patch; + +#ifdef HWRENDER + Patch_CreateGL(patch); + return (void *)patch; +#endif +} diff --git a/src/w_wad.h b/src/w_wad.h index 55e3e0072..5e23ff0a7 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -92,6 +92,7 @@ typedef struct { virtres_t* vres_GetMap(lumpnum_t); void vres_Free(virtres_t*); virtlump_t* vres_Find(const virtres_t*, const char*); +void* vres_GetPatch(virtlump_t *vlump, INT32 tag); // ========================================================================= // DYNAMIC WAD LOADING @@ -154,6 +155,7 @@ const char *W_CheckNameForNum(lumpnum_t lumpnum); UINT16 W_FindNextEmptyInPwad(UINT16 wad, UINT16 startlump); // checks only in one pwad +UINT16 W_CheckNumForMapPwad(const char *name, UINT16 wad, UINT16 startlump); UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump); // checks only in one pwad UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump); diff --git a/src/y_inter.c b/src/y_inter.c index 231dcc9c3..34097718e 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -111,7 +111,6 @@ typedef struct char str[62]; UINT8 gtc; const char *gts; - patch_t *pic; boolean encore; } y_votelvlinfo; @@ -834,8 +833,8 @@ void Y_StartIntermission(void) //if (dedicated) return; // This should always exist, but just in case... - if (!mapheaderinfo[prevmap]) - P_AllocMapHeader(prevmap); + if (prevmap >= nummapheaders || !mapheaderinfo[prevmap]) + I_Error("Y_StartIntermission: Internal map ID %d not found (nummapheaders = %d)", prevmap, nummapheaders); switch (intertype) { @@ -1011,7 +1010,18 @@ void Y_VoteDrawer(void) else { str = levelinfo[i].str; - pic = levelinfo[i].pic; + + pic = NULL; + + if (mapheaderinfo[votelevels[i][0]]) + { + pic = mapheaderinfo[votelevels[i][0]]->thumbnailPic; + } + + if (!pic) + { + pic = blanklvl; + } } if (selected[i]) @@ -1131,9 +1141,23 @@ void Y_VoteDrawer(void) patch_t *pic; if (votes[i] >= 3 && (i != pickedvote || voteendtic == -1)) + { pic = randomlvl; + } else - pic = levelinfo[votes[i]].pic; + { + pic = NULL; + + if (mapheaderinfo[votelevels[votes[i]][0]]) + { + pic = mapheaderinfo[votelevels[votes[i]][0]]->thumbnailPic; + } + + if (!pic) + { + pic = blanklvl; + } + } if (!timer && i == voteclient.ranim) { @@ -1492,8 +1516,6 @@ void Y_StartVote(void) for (i = 0; i < 4; i++) { - lumpnum_t lumpnum; - // set up the encore levelinfo[i].encore = (votelevels[i][1] & VOTEMODIFIER_ENCORE); votelevels[i][1] &= ~VOTEMODIFIER_ENCORE; @@ -1534,13 +1556,6 @@ void Y_StartVote(void) levelinfo[i].gts = Gametype_Names[votelevels[i][1]]; else levelinfo[i].gts = NULL; - - // set up the pic - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(votelevels[i][0]+1))); - if (lumpnum != LUMPERROR) - levelinfo[i].pic = W_CachePatchName(va("%sP", G_BuildMapName(votelevels[i][0]+1)), PU_STATIC); - else - levelinfo[i].pic = W_CachePatchName("BLANKLVL", PU_STATIC); } voteclient.loaded = true; @@ -1561,8 +1576,6 @@ void Y_EndVote(void) // static void Y_UnloadVoteData(void) { - UINT8 i; - voteclient.loaded = false; if (rendermode != render_soft) @@ -1577,28 +1590,6 @@ static void Y_UnloadVoteData(void) UNLOAD(cursor4); UNLOAD(randomlvl); UNLOAD(rubyicon); - - // to prevent double frees... - for (i = 0; i < 4; i++) - { - // I went to all the trouble of doing this, - // but literally nowhere else frees level pics. -#if 0 - UINT8 j; - - if (!levelinfo[i].pic) - continue; - - for (j = i+1; j < 4; j++) - { - if (levelinfo[j].pic == levelinfo[i].pic) - levelinfo[j].pic = NULL; - } - UNLOAD(levelinfo[i].pic); -#else - CLEANUP(levelinfo[i].pic); -#endif - } } //