diff --git a/graphics/Games/MadcatTiles.png b/graphics/Games/MadcatTiles.png new file mode 100644 index 000000000..a2a4b1e49 Binary files /dev/null and b/graphics/Games/MadcatTiles.png differ diff --git a/language.def_menu b/language.def_menu index 08d410b62..8780a8dfe 100644 --- a/language.def_menu +++ b/language.def_menu @@ -643,6 +643,9 @@ SWWM_HELPTXT = "\cx———————————————————————\c-\n" "\n" "\cfUp/Down:\c- Scroll"; +SWWM_GAMETAB = "Games"; +SWWM_PICKGAME = "Choose a game"; +SWWM_GAMETITLE_MADCATGAME = "Boot Test"; // Wallbuster menu SWWM_BUSTERTITLE = "Wallbuster - Easy Reload Menu"; SWWM_BUSTERKEYS = diff --git a/language.es_menu b/language.es_menu index b544d7272..b41b5bf85 100644 --- a/language.es_menu +++ b/language.es_menu @@ -622,6 +622,9 @@ SWWM_HELPTXT = "\cx———————————————————————————————\c-\n" "\n" "\cfArriba/Abajo:\c- Scroll"; +SWWM_GAMETAB = "Juegos"; +SWWM_PICKGAME = "Elige un juego"; +SWWM_GAMETITLE_MADCATGAME = "Test de Arranque"; // Wallbuster menu SWWM_BUSTERTITLE = "Wallbuster - Menú de Recarga Fácil"; SWWM_BUSTERKEYS = diff --git a/language.version b/language.version index 0deea0198..49359a1d0 100644 --- a/language.version +++ b/language.version @@ -1,3 +1,3 @@ [default] -SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r30 \cu(Thu 14 Apr 22:14:59 CEST 2022)\c-"; -SWWM_SHORTVER="\cw1.3pre r30 \cu(2022-04-14 22:14:59)\c-"; +SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r31 \cu(Fri 15 Apr 18:30:29 CEST 2022)\c-"; +SWWM_SHORTVER="\cw1.3pre r31 \cu(2022-04-15 18:30:29)\c-"; diff --git a/sndinfo.txt b/sndinfo.txt index 2018d1196..e3a48b961 100644 --- a/sndinfo.txt +++ b/sndinfo.txt @@ -1601,3 +1601,6 @@ wolf3d/pushwall sounds/wolfstuff/wolf3d_pushwall.ogg wolf3d/ssdie sounds/wolfstuff/wolf3d_ssdie.ogg wolf3d/ssfire sounds/wolfstuff/wolf3d_ssfire.ogg wolf3d/sssight sounds/wolfstuff/wolf3d_sssight.ogg + +// minigames +madcat/boot sounds/MADCAT.ogg diff --git a/sounds/MADCAT.ogg b/sounds/MADCAT.ogg new file mode 100644 index 000000000..d9d80fbfd Binary files /dev/null and b/sounds/MADCAT.ogg differ diff --git a/zscript.txt b/zscript.txt index b2cd4d723..a3fc9d1ff 100644 --- a/zscript.txt +++ b/zscript.txt @@ -75,6 +75,10 @@ version "4.8" #include "zscript/kbase/swwm_kbasetab_chat.zsc" #include "zscript/kbase/swwm_kbasetab_help.zsc" #include "zscript/kbase/swwm_kbasetab_secret.zsc" +#include "zscript/kbase/swwm_kbasetab_games.zsc" +// minigames +#include "zscript/games/swwm_madcat.zsc" +#include "zscript/games/swwm_minigames.zsc" // items #include "zscript/items/swwm_baseitem.zsc" #include "zscript/items/swwm_basehealth.zsc" diff --git a/zscript/games/swwm_madcat.zsc b/zscript/games/swwm_madcat.zsc new file mode 100644 index 000000000..4cda13ac5 --- /dev/null +++ b/zscript/games/swwm_madcat.zsc @@ -0,0 +1,262 @@ +// shared MADCAT framework code + +// skeleton class, doesn't have much other than the bare generic essentials +Class MadcatGameState abstract ui +{ + Name GameName; + + abstract MadcatGameState Init(); +} + +// state manager static thinker, to keep progress throughout a play session +// must be created by the static handler on map load if it doesn't exist +Class MadcatGameStateManager : Thinker +{ + ui Array GameState; + + // return state object for a specific game name + // returns null if no state exists (caller must add a new one) + static ui MadcatGameState GetState( Name GameName ) + { + let gsm = MadcatGameStateManager(ThinkerIterator.Create("MadcatGameStateManager").Next()); + if ( !gsm ) ThrowAbortException("Game State Manager not found."); + for ( int i=0; i= 18 ) continue; + if ( boot_tiles[j+147] && (boot_tiles[j+147] < 195) ) boot_tiles[j+147] += 21; + } + for ( int j=-1; j= 18 ) continue; + if ( (boot_tiles[j+171] < 195) ) boot_tiles[j+171] += 21; + } + for ( int j=-2; j= 18 ) continue; + if ( (boot_tiles[j+195] < 195) ) boot_tiles[j+195] += 21; + } + } + private void Boot_ClearTiles() + { + for ( int i=0; i<360; i++ ) boot_tiles[i] = 0; + } + + // game has been booted up + virtual void Init() + { + oldmus = musplaying.name; + oldorder = musplaying.baseorder; + oldloop = musplaying.loop; + S_ChangeMusic(""); + bClosed = false; + global_state = CAT_BOOT; + boot_state = BS_FADEIN; + boot_timer = -16; + boot_tileset = TexMan.CheckForTexture("graphics/Games/MadcatTiles.png",TexMan.Type_Any); + } + + // game is ticking + virtual void Tick() + { + // only bootup is handled here + if ( global_state != CAT_BOOT ) return; + switch ( boot_state ) + { + case BS_FADEIN: + if ( boot_timer == 0 ) Boot_PrepareTitle(); + else if ( !(boot_timer%4) && (boot_timer >= 4) ) Boot_FadeInTiles(); + boot_timer++; + if ( boot_timer > 12 ) + { + boot_state++; + boot_timer = -16; + } + break; + case BS_FLASH: + Boot_FlashState(); + boot_timer++; + if ( boot_timer > 80 ) + { + boot_state++; + boot_timer = 0; + } + else if ( boot_timer == 0 ) S_StartSound("madcat/boot",CHAN_VOICE,CHANF_UI,1.,0.); + break; + case BS_FADEOUT: + if ( boot_timer == 0 ) + { + Boot_PrepareTitle(); + Boot_PreFadeOutTiles(); + } + else if ( !(boot_timer%4) && (boot_timer >= 4) ) Boot_FadeOutTiles(); + boot_timer++; + if ( boot_timer > 12 ) + { + boot_state++; + boot_timer = 0; + } + break; + case BS_IDLE: + boot_timer++; + if ( boot_timer == 4 ) Boot_ClearTiles(); + else if ( boot_timer >= 40 ) + { + global_state = CAT_MENU; + boot_state = 0; + boot_timer = 0; + } + break; + } + } + + // draw on the virtual screen + // coordinates are absolute + virtual void Draw( Vector2 screen_pos, double screen_zoom ) + { + // only bootup is handled here + if ( global_state != CAT_BOOT ) return; + for ( int i=0; i<360; i++ ) + { + int tsx = (boot_tiles[i]&0x0F)*8; + int tsy = ((boot_tiles[i]&0xF0)>>4)*8; + int fsx = (i%24)*16; + int fsy = (i/24)*16; + Screen.DrawTexture(boot_tileset,false, + screen_pos.x+fsx*screen_zoom, + screen_pos.y+fsy*screen_zoom, + DTA_ScaleX,screen_zoom,DTA_ScaleY,screen_zoom, + DTA_SrcX,tsx,DTA_SrcY,tsy, + DTA_SrcWidth,8,DTA_SrcHeight,8, + DTA_DestWidth,16,DTA_DestHeight,16); + } + } + + // process keyboard input + virtual bool ProcessInput( int key, bool release ) + { + return false; + } + + // game has been closed + virtual void Close() + { + bClosed = true; + S_ChangeMusic(oldmus,oldorder,oldloop); + } + + override void OnDestroy() + { + if ( !bClosed ) Close(); + Super.OnDestroy(); + } +} diff --git a/zscript/games/swwm_minigames.zsc b/zscript/games/swwm_minigames.zsc new file mode 100644 index 000000000..6d4970f01 --- /dev/null +++ b/zscript/games/swwm_minigames.zsc @@ -0,0 +1 @@ +// basic minigames diff --git a/zscript/kbase/swwm_kbase.zsc b/zscript/kbase/swwm_kbase.zsc index a9c96368a..1bcf298de 100644 --- a/zscript/kbase/swwm_kbase.zsc +++ b/zscript/kbase/swwm_kbase.zsc @@ -188,6 +188,7 @@ Class DemolitionistMenu : GenericMenu 'DemolitionistLibraryTab', 'DemolitionistStoreTab', 'DemolitionistChatTab', + 'DemolitionistGameTab', 'DemolitionistHelpTab', 'DemolitionistSecretTab' }; @@ -218,44 +219,48 @@ Class DemolitionistMenu : GenericMenu override bool MenuEvent( int mkey, bool fromcontroller ) { - switch ( kcode ) + // pachinko code only handled if the tab lacks direct input + if ( !tabs[curtab].bDirectInput ) { - case 0: - case 1: - if ( mkey == MKEY_UP ) kcode++; - else kcode = 0; - break; - case 2: - case 3: - if ( mkey == MKEY_DOWN ) kcode++; - else kcode = 0; - break; - case 4: - case 6: - if ( mkey == MKEY_LEFT ) kcode++; - else kcode = 0; - break; - case 5: - case 7: - if ( mkey == MKEY_RIGHT ) kcode++; - else kcode = 0; - break; - case 10: - if ( mkey == MKEY_ENTER ) + switch ( kcode ) { - int secret = FindTabType('DemolitionistSecretTab'); - if ( curtab != secret ) + case 0: + case 1: + if ( mkey == MKEY_UP ) kcode++; + else kcode = 0; + break; + case 2: + case 3: + if ( mkey == MKEY_DOWN ) kcode++; + else kcode = 0; + break; + case 4: + case 6: + if ( mkey == MKEY_LEFT ) kcode++; + else kcode = 0; + break; + case 5: + case 7: + if ( mkey == MKEY_RIGHT ) kcode++; + else kcode = 0; + break; + case 10: + if ( mkey == MKEY_ENTER ) { - MenuSound("misc/secret"); - tabs[curtab].OnDeselect(); - curtab = secret; - tabs[curtab].OnSelect(); - } + int secret = FindTabType('DemolitionistSecretTab'); + if ( curtab != secret ) + { + MenuSound("misc/secret"); + tabs[curtab].OnDeselect(); + curtab = secret; + tabs[curtab].OnSelect(); + } + } + default: + kcode = 0; + break; } - default: - kcode = 0; - break; } switch ( mkey ) { @@ -384,6 +389,8 @@ Class DemolitionistMenu : GenericMenu override bool OnUiEvent( UIEvent ev ) { + if ( tabs[curtab].bDirectInput && ((ev.type == UIEvent.Type_KeyDown) || (ev.type == UIEvent.Type_KeyUp)) ) + return tabs[curtab].DirectInput(ev); switch ( ev.type ) { case UIEvent.Type_KeyDown: diff --git a/zscript/kbase/swwm_kbase_tab.zsc b/zscript/kbase/swwm_kbase_tab.zsc index 57317baa4..0ab4934b2 100644 --- a/zscript/kbase/swwm_kbase_tab.zsc +++ b/zscript/kbase/swwm_kbase_tab.zsc @@ -5,6 +5,7 @@ Class DemolitionistMenuTab ui abstract DemolitionistMenu master; String title; bool bHidden; // tab does not display and can't be selected + bool bDirectInput; // inputs are directly passed to this tab // tab initialization virtual DemolitionistMenuTab Init( DemolitionistMenu master ) @@ -23,6 +24,12 @@ Class DemolitionistMenuTab ui abstract { } + // called for "direct input" from ui events + virtual bool DirectInput( UIEvent ev ) + { + return false; + } + // called after this tab is selected virtual void OnSelect() { diff --git a/zscript/kbase/swwm_kbasetab_games.zsc b/zscript/kbase/swwm_kbasetab_games.zsc new file mode 100644 index 000000000..7de6e1788 --- /dev/null +++ b/zscript/kbase/swwm_kbasetab_games.zsc @@ -0,0 +1,148 @@ +// a game inside a game? + +Class DemolitionistGameTab : DemolitionistMenuTab +{ + MadcatGame game; + Array > gamelist; + int sel; + + override DemolitionistMenuTab Init( DemolitionistMenu master ) + { + title = StringTable.Localize("$SWWM_GAMETAB"); + gamelist.Push((Class)('MadcatGame')); // test + bDirectInput = true; + return Super.Init(master); + } + + override void OnDestroy() + { + Super.OnDestroy(); + if ( game ) game.Destroy(); + } + + override void OnSelect() + { + sel = master.shnd.menustate.At("LastGame").ToInt(); + } + override void OnDeselect() + { + master.shnd.menustate.Insert("LastGame",String.Format("%d",sel)); + if ( game ) game.Destroy(); + } + + override void MenuInput( int key ) + { + // while a game is running, only the back button can be used + if ( game ) + { + if ( key == MK_BACK ) + { + game.Destroy(); + master.MenuSound("menu/democlose"); + } + return; + } + switch ( key ) + { + case MK_DOWN: + if ( sel > 0 ) + { + sel--; + master.MenuSound("menu/demoscroll"); + } + break; + case MK_UP: + if ( sel < gamelist.Size()-1 ) + { + sel++; + master.MenuSound("menu/demoscroll"); + } + break; + case MK_ENTER: + master.MenuSound("menu/demosel"); + game = MadcatGame(new(gamelist[sel])); + game.Init(); + break; + } + } + + override void MouseInput( Vector2 pos, int btn ) + { + if ( game ) return; + if ( btn != MB_LEFT ) return; + String str; + double xx, yy; + yy = int(master.ws.y-14*gamelist.Size())/2; + for ( int i=0; i yy+h ) continue; + sel = i; + MenuInput(MK_ENTER); + break; + } + } + + override bool DirectInput( UIEvent ev ) + { + if ( !game ) return false; + return game.ProcessInput(ev.keychar,ev.type==UIEvent.Type_KeyUp); + } + + override void Ticker() + { + if ( game ) game.Tick(); + } + + override void Drawer() + { + if ( game ) + { // calculate res to fit + double scl = max(floor(((master.ws.y-120)*master.hs)/240.),1.); + Vector2 res = ((384,240)*scl)/master.hs; + String str = StringTable.Localize("$SWWM_GAMETITLE_"..game.GetClassName()); + double xx = int(master.ws.x-master.mSmallFont.StringWidth(str))/2; + double yy = int(master.ws.y-res.y)/2; + Screen.DrawText(master.mSmallFont,Font.CR_FIRE,master.origin.x+xx,master.origin.y+yy-32,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true); + int cw = int(ceil((master.mSmallFont.StringWidth(str)+8)/6.))*6; + xx = int(master.ws.x-cw)/2; + for ( int i=0; i=4)?0x2727:0x2726,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true); + Screen.DrawChar(master.mSmallFont,Font.CR_GREEN,master.origin.x+xx+w+6,master.origin.y+yy,((gametic&8)>=4)?0x2727:0x2726,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true); + } + yy += 14; + } + } + } +}