From 8f2d59ca9f7aaf96d92bdd2a407a398323a7b811 Mon Sep 17 00:00:00 2001 From: Emong Date: Sun, 28 Jul 2019 00:01:24 -0400 Subject: [PATCH 1/2] Joystick support + some other fixes --- GameData/messages.txt | 2 +- arenahq.pp | 113 +++++++++++- colormenu.pp | 2 +- gflooker.pp | 2 +- gharena.pas | 8 +- history.txt | 8 + pcaction.pp | 69 ++++++- readme.md | 4 + sdlgfx.pp | 421 ++++++++++++++++++++++++++++++++++++++---- sdlinfo.pp | 2 +- sdlmap.pp | 72 +++++++- sdlmenus.pp | 2 +- texutil.pp | 22 ++- ui4gh.pp | 246 +++++++++++++++++++++++- 14 files changed, 906 insertions(+), 67 deletions(-) diff --git a/GameData/messages.txt b/GameData/messages.txt index 003d583..a44083e 100644 --- a/GameData/messages.txt +++ b/GameData/messages.txt @@ -649,4 +649,4 @@ ColorSet_3 ColorSet_4 ColorSet_5 ColorSet_6
- +CONFIG_Prompt diff --git a/arenahq.pp b/arenahq.pp index bdab363..0120c50 100644 --- a/arenahq.pp +++ b/arenahq.pp @@ -24,7 +24,7 @@ interface {$IFDEF SDLMODE} -uses sdlgfx; +uses sdlgfx {$IFDEF JOYSTICK_SUPPORT},SDL{$ENDIF}; {$ENDIF} const @@ -37,6 +37,9 @@ interface {$IFDEF SDLMODE} Procedure StartRPGCampaign( RD: RedrawProcedureType ); Procedure DesignDirBrowser( RD: RedrawProcedureType ); +{$IFDEF JOYSTICK_SUPPORT} +Procedure ConfigureController(const RD: RedrawProcedureType); +{$ENDIF} {$ELSE} Procedure StartRPGCampaign; Procedure DesignDirBrowser; @@ -1397,6 +1400,114 @@ procedure HQMain( HQCamp: CampaignPtr ); CleanSpriteList(); end; +{$IFDEF JOYSTICK_SUPPORT} +Procedure ConfigRedraw(const RD: RedrawProcedureType; constref button: TButtonMapDesc; constref msg: String); + { Draw the config UI } + { Assumes that button is not null } +var + PromptRect, DisplayRect: TSDL_Rect; + S: String; +begin + PromptRect := ZONE_ConfigButtonPrompt.GetRect(); + DisplayRect := ZONE_ConfigButton.GetRect(); + S := 'Press ' + Replace(button.ConfigName, 'Button', ''); + if button.MappedCmd <> NIL then S := S + ' (' + button.MappedCmd^.CmdName + ')'; + + RD; + InfoBox(PromptRect); + InfoBox(DisplayRect); + CMessage(S, DisplayRect, StdWhite); + CMessage(msg, PromptRect, StdWhite); + GHFlip; +end; + +Procedure ConfigureController(const RD: RedrawProcedureType); +const + GHA_SKIP_DELAY = 300; + SDL_CARDINAL_DIRS = [SDL_HAT_UP, SDL_HAT_DOWN, SDL_HAT_LEFT, SDL_HAT_RIGHT]; +var + i, num: Integer; + event: TSDL_Event; + prevTicks: LongWord; + loop, quit: Boolean; + kind: TButtonType; + msg: String[150]; + dir: ShortInt; +begin + msg := MsgString('CONFIG_Prompt'); + quit := false; + + for i := ord(BUTTON_A) to ord(BUTTON_RIGHT) do begin + loop := true; + num := -1; + kind := TYPE_NONE; + prevTicks := 0; + dir := 0; + while loop do begin + ConfigRedraw(RD, ButtonMap[i], msg); + + {Bootleg extra poll event loop, since we need the raw button indexes and not the RPGKey output} + if SDL_PollEvent(@event) = 1 then begin + case event.type_ of + SDL_JOYBUTTONDOWN: begin + prevTicks := SDL_GetTicks; + num := event.jbutton.button; + kind := TYPE_BUTTON; + end; + SDL_JOYBUTTONUP: if num = event.jbutton.button then loop := false; + SDL_JOYHATMOTION: begin + if event.jhat.value in SDL_CARDINAL_DIRS then begin + prevTicks := SDL_GetTicks; + num := event.jhat.hat; + dir := event.jhat.value; + kind := TYPE_HAT; + end else if event.jhat.value = SDL_HAT_CENTERED then begin + if num = event.jhat.hat then loop := false; + end; + end; + SDL_JOYAXISMOTION: begin + {special case around the main analog stick} + if (event.jaxis.axis <> JoyXIndex) + and (event.jaxis.axis <> JoyYIndex) then begin + if abs(event.jaxis.value) > JOY_AxisCutoff then begin + prevTicks := SDL_GetTicks; + num := event.jaxis.axis; + dir := Sgn(event.jaxis.value); + kind := TYPE_AXIS; + end else if num = event.jaxis.axis then loop := false; + end; + end; + SDL_VIDEORESIZE: ResizeScreen(event.resize.w, event.resize.h); + SDL_KEYDOWN: if event.key.keysym.sym = SDLK_ESCAPE then begin + quit := true; + break; + end; + end; + end else begin + {skip button if any button is held down} + if (num <> -1) and ((SDL_GetTicks - prevTicks) > GHA_SKIP_DELAY) then begin + num := -1; + kind := TYPE_BUTTON; + dir := 0; + loop := false; + end; + + CheckAnimTicks; + end; + end; + + if quit then break; + + ButtonMap[i].BCode := num; + ButtonMap[i].BDir := dir; + ButtonMap[i].BType := kind; + end; + + {here to keep from confusing the main event loop} + ButtonState := []; +end; +{$ENDIF} + {$ELSE} Procedure BrowseDesignFile( List: GearPtr ); { Choose one of the sibling gears from LIST and display its properties. } diff --git a/colormenu.pp b/colormenu.pp index c9c876a..34307cb 100644 --- a/colormenu.pp +++ b/colormenu.pp @@ -336,7 +336,7 @@ implementation ColorMenuRedraw; GHFlip; - a := RPGKey; + a := RPGKey(CONTEXT_MENU); if a = #8 then begin a := #27; diff --git a/gflooker.pp b/gflooker.pp index 82b15c4..7bcf375 100644 --- a/gflooker.pp +++ b/gflooker.pp @@ -352,7 +352,7 @@ implementation IndicateTile( GB , X , Y ); {$ENDIF} - A := RPGKey; + A := RPGKey{$IFDEF SDLMODE}(CONTEXT_LOOKER){$ENDIF}; if A = KeyMap[ KMC_North ].KCode then begin RepositionCursor( 6 ); diff --git a/gharena.pas b/gharena.pas index e7e912a..58fa788 100644 --- a/gharena.pas +++ b/gharena.pas @@ -36,7 +36,7 @@ {$ENDIF} const - Version = '1.310'; + Version = '1.JOY'; var RPM: RPGMenuPtr; @@ -89,6 +89,9 @@ AddRPGMenuItem( RPM , 'View Design Files' , 7 ); {$IFDEF SDLMODE} AddRPGMenuItem( RPM , 'View Color Selector' , 8 ); +{$IFDEF JOYSTICK_SUPPORT} + if HasJoystick then AddRPGMenuItem( RPM , 'Configure Controller' , 9 ); +{$ENDIF} {$ENDIF} AddRPGMenuItem( RPM , 'Quit Game' , -1 ); @@ -128,6 +131,9 @@ {$IFDEF SDLMODE} 7: DesignDirBrowser( @RedrawOpening ); 8: DoCosplay; +{$IFDEF JOYSTICK_SUPPORT} + 9: ConfigureController(@RedrawOpening); +{$ENDIF} {$ELSE} 7: DesignDirBrowser; {$ENDIF} diff --git a/history.txt b/history.txt index 9d25ecf..f2f3315 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,11 @@ +1.JOY X X 2019 +- Somewhat sketchy controller support can be compiled in with -dJOYSTICK_SUPPORT (a lot of files) + - Button bindings can be set during play, actions associated with buttons can be set in the config file. +- Resolution selector added to Play Options (mostly for fullscreen mode) (sdlgfx.pp, ui4gh.pp) +- NOKEYBOARD option in config file to use on-screen keyboard instead of typing (sdlgfx.pp, ui4gh.pp) +- Some string utility functions rewritten to use sets (texutil.pp) +- Fix issue preventing ADVANCEDCOLORS from being enabled [but not any issues with ADVANCEDCOLORS] (ui4gh.pp) + 1.310 February 7 2019 - All old style walls have been replaced by thin walls (sdlmap.pp) - Portraits may be assigned as unisex "por_n_*.png" (sdlinfo.pp) diff --git a/pcaction.pp b/pcaction.pp index 932330f..0c1261d 100644 --- a/pcaction.pp +++ b/pcaction.pp @@ -68,6 +68,15 @@ implementation SDLCombatDisplay( PCACTIONRD_GB ); end; +{$IFDEF JOYSTICK_SUPPORT} +Procedure PCActionRedrawWithJoystick; + { Redraw the map and the PC's info, adding an indicator for joystick direction. } +begin + JoystickIndicatorForRedraw; + SDLCombatDisplay( PCACTIONRD_GB ); +end; +{$ENDIF} + Procedure PhoneRedraw; { Redraw the map and the PC's info. } begin @@ -309,6 +318,30 @@ implementation InsertInvCom( PC , NPC ); end; +{$IFDEF SDLMODE} +Procedure SetResolution(); +var + RPM: RPGMenuPtr; + N: Integer; +begin + N := 0; + RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CenterMenu ); + + for N := 0 to high(AvailableModes) do begin + AddRPGMenuItem( RPM , AvailableModes[N].Name , N ); + end; + + SetItemByValue( RPM , ModeIndex ); + + N := SelectMenu( RPM , @CenterMenuRedraw ); + + if N in [0 .. high(AvailableModes)] then begin + ResizeScreen(AvailableModes[N].Width, AvailableModes[N].Height); + ModeIndex := N; + end; +end; +{$ENDIF} + Procedure SetPlayOptions( GB: GameBoardPtr; Mek: GearPtr ); { Allow the player to set control type, default burst value settings, } { and whatever other stuff you think is appropriate. } @@ -350,6 +383,7 @@ implementation end else begin AddRPGMenuItem( RPM , 'Enable Name Display' , 9 ); end; + AddRPGMenuItem(RPM, 'Set Resolution', 11); {$ELSE} if Accessibility_On then begin AddRPGMenuItem( RPM , 'Disable Accessibility+' , 10 ); @@ -404,6 +438,10 @@ implementation end else if N = 10 then begin Accessibility_On := Not Accessibility_On; + {$IFDEF SDLMODE} + end else if N = 11 then begin + SetResolution(); + {$ENDIF} end; until N = -1; @@ -912,7 +950,7 @@ implementation if PropD < 0 then begin DialogMsg( MsgString( 'PCUS_Prompt' ) ); {$IFDEF SDLMODE} - PropD := DirKey( @PCActionRedraw ); + PropD := DirKey( @{$IFNDEF JOYSTICK_SUPPORT}PCActionRedraw{$ELSE}PCActionRedrawWithJoystick{$ENDIF} ); {$ELSE} PropD := DirKey; {$ENDIF} @@ -943,7 +981,7 @@ implementation P := GearCurrentLocation( PC ); DialogMsg( MsgString( 'PCUSOP_Prompt' ) ); {$IFDEF SDLMODE} - PropD := DirKey( @PCActionRedraw ); + PropD := DirKey( @{$IFNDEF JOYSTICK_SUPPORT}PCActionRedraw{$ELSE}PCActionRedrawWithJoystick{$ENDIF} ); {$ELSE} PropD := DirKey; {$ENDIF} @@ -968,7 +1006,7 @@ implementation begin DialogMsg( MsgString( 'PCREPAIR_Prompt' ) ); {$IFDEF SDLMODE} - D := DirKey( @PCActionRedraw ); + D := DirKey( @{$IFNDEF JOYSTICK_SUPPORT}PCActionRedraw{$ELSE}PCActionRedrawWithJoystick{$ENDIF} ); {$ELSE} D := DirKey; {$ENDIF} @@ -1115,7 +1153,7 @@ implementation if D > 1 then begin DialogMsg( MsgString( 'DOMINATE_Prompt' ) ); {$IFDEF SDLMODE} - D := DirKey( @PCActionRedraw ); + D := DirKey( @{$IFNDEF JOYSTICK_SUPPORT}PCActionRedraw{$ELSE}PCActionRedrawWithJoystick{$ENDIF} ); {$ELSE} D := DirKey; {$ENDIF} @@ -1232,7 +1270,7 @@ implementation if D > 1 then begin DialogMsg( MsgString( 'PICKPOCKET_Prompt' ) ); {$IFDEF SDLMODE} - D := DirKey( @PCActionRedraw ); + D := DirKey( @{$IFNDEF JOYSTICK_SUPPORT}PCActionRedraw{$ELSE}PCActionRedrawWithJoystick{$ENDIF} ); {$ELSE} D := DirKey; {$ENDIF} @@ -3039,6 +3077,7 @@ procedure ShiftGears( GB: GameBoardPtr; Mek: GearPtr ); { Indicate the mek to get the action for, } { and prepare the display. } {$IFDEF SDLMODE} + ClearOverlay; IndicateTile( GB , Mek , True ); {$ELSE} DisplayGearInfo( Mek , gb ); @@ -3239,6 +3278,9 @@ procedure ShiftGears( GB: GameBoardPtr; Mek: GearPtr ); GotMove: Boolean; Mobile: Boolean; P: Point; + {$IFDEF SDLMODE} + DidRefresh: Boolean; + {$ENDIF} begin { The original comment said "Record where the mek currently is". } { But this is obviously checking to see if the mecha is mobile. } @@ -3394,11 +3436,26 @@ procedure ShiftGears( GB: GameBoardPtr; Mek: GearPtr ); IndicateTile( Camp^.GB , Mek , True ); P := MouseMapPos; - if OnTheMap( P.X , P.Y ) and Mouse_Active then begin + DidRefresh := Mouse_Active and OnTheMap( P.X , P.Y ); + if DidRefresh then begin + ClearOverlay; MouseAtTile( Camp^.GB , P.X , P.Y ); end; + + {$IFDEF JOYSTICK_SUPPORT} + {TODO: make the tile info thing pop up based on this?} + if not DidRefresh then ClearOverlay; + JoystickIndicate(Mek); + + if not Mouse_Active then P := JoystickIndicatorPos(Mek); + {$ENDIF} + SDLCombatDisplay( Camp^.GB ); + {$IFNDEF JOYSTICK_SUPPORT} if OnTheMap( P.X , P.Y ) and Mouse_Active then begin + {$ELSE} + if (Mouse_Active or ((not Mouse_Active) and (JoyAxisDir <> []))) and OnTheMap( P.X , P.Y ) then begin + {$ENDIF} DisplayTileInfo(Camp^.GB, P.X, P.Y, False ); end; { Everybody do the flip. } diff --git a/readme.md b/readme.md index d281932..eb075ca 100644 --- a/readme.md +++ b/readme.md @@ -27,6 +27,10 @@ Open a terminal in the folder with the source code and type: fpc -dSDLMODE gharena +For controller support in SDL mode, type: + + fpc -dSDLMODE -dJOYSTICK_SUPPORT gharena + For the ASCII version, just type: fpc gharena diff --git a/sdlgfx.pp b/sdlgfx.pp index 6b8fc8b..aca93b5 100644 --- a/sdlgfx.pp +++ b/sdlgfx.pp @@ -41,6 +41,14 @@ interface function GetRect: TSDL_Rect; end; + TSDLMode = packed record + Name: String[10]; + Width, Height: Word; + end; + + TSDLModeArray = array of TSDLMode; + + TInputContext = (CONTEXT_NONE, CONTEXT_MENU, CONTEXT_LOOKER); const StdBlack: TSDL_Color = ( r: 0; g: 0; b: 0 ); @@ -107,6 +115,8 @@ interface ZONE_TextInputBigBox: DynamicRect = ( dx:-220; dy:-61; w:440; h:56; anchor: ANC_middle ); ZONE_PhoneInstructions: DynamicRect = ( dx:-200; dy:15; w:400; h:16; anchor: ANC_middle ); + ZONE_KeyboardInput: DynamicRect = ( dx:-220; dy:20; w:440; h:100; anchor: ANC_middle ); + ZONE_InteractStatus: DynamicRect = ( dx:-250; dy: -210; w: 395; h: 40; anchor: ANC_middle ); ZONE_InteractMsg: DynamicRect = ( dx: -250; dy:-120; w:395; h: 110; anchor: ANC_middle ); ZONE_InteractMenu: DynamicRect = ( dx: -250; dy:-5; w:500; h: 120; anchor: ANC_middle ); @@ -157,6 +167,11 @@ interface ZONE_YesNoPrompt: DynamicRect = ( dx:-175; dy:-150; w:350; h:200; anchor: ANC_middle ); ZONE_YesNoMenu: DynamicRect = ( dx:-175; dy:55; w:350; h:50; anchor: ANC_middle ); + {$IFDEF JOYSTICK_SUPPORT} + ZONE_ConfigButton: DynamicRect = ( dx:-100; dy:55; w:200; h:25; anchor: ANC_middle ); + ZONE_ConfigButtonPrompt: DynamicRect = ( dx:-250; dy:0; w:500; h:40; anchor: ANC_middle ); + {$ENDIF} + Console_History_Length = 240; GH_REPEAT_DELAY = 200; @@ -170,6 +185,13 @@ interface CS_SecondaryMecha = 5; CS_Detailing = 6; + {Base constant for requesting SDL surfaces} + SDL_GHABASE = SDL_HWSURFACE or SDL_DOUBLEBUF; + + {$IFDEF JOYSTICK_SUPPORT} + {how far you need to move the stick to make it count} + JOY_AxisCutoff = High(SmallInt) / 2; + {$ENDIF} var Game_Screen: PSDL_Surface; @@ -181,11 +203,17 @@ interface Mouse_X, Mouse_Y: LongInt; { Current mouse position. } Animation_Phase: Integer; Last_Clock_Update: UInt32; - + AvailableModes: TSDLModeArray; + {$IFDEF JOYSTICK_SUPPORT} + HasJoystick: Boolean = false; + JoyAxisDir: TButtonSet; + ButtonState: TButtonSet; + {$ENDIF} + MasterColorList: SAttPtr; - Music_List: SAttPtr; -{ MyMusic: P_Mix_Music;} +{ Music_List: SAttPtr; + MyMusic: P_Mix_Music;} Function RandomColorString( ColorSet: Integer ): String; @@ -203,15 +231,20 @@ procedure DrawAlphaSprite( Spr: SensibleSpritePtr; MyDest: TSDL_Rect; Frame: Int Procedure FillRectWithSprite( MyRect: TSDL_Rect; MySprite: SensibleSpritePtr; MyFrame: Integer ); -function RPGKey: Char; +function RPGKey: Char inline; +function RPGKey(context: TInputContext): Char; Procedure ClrZone( var Z: TSDL_Rect ); Procedure ClrScreen; +Procedure ResizeScreen(width, height: Integer) inline; +Procedure CheckAnimTicks() inline; Function PrettyPrint( msg: string; Width: Integer; var FG: TSDL_Color; DoCenter: Boolean; MyFont: PTTF_Font ): PSDL_Surface; +Function BlockPrint( const msg: SAttPtr; Width: Integer; var FG: TSDL_Color; DoCenter: Boolean; MyFont: PTTF_Font ): PSDL_Surface; Procedure QuickText( const msg: String; MyDest: TSDL_Rect; Color: TSDL_Color ); Procedure QuickTinyText( const msg: String; MyDest: TSDL_Rect; Color: TSDL_Color ); Procedure CMessage( const msg: String; Z: TSDL_Rect; C: TSDL_Color ); +Procedure BMessage( const msg: SAttPtr; Z: TSDL_Rect; C: TSDL_Color; font: PTTF_Font ); Procedure GameMSG( const msg: string; Z: TSDL_Rect; var C: TSDL_Color ); Procedure GameMSG( const msg: string; Z: TSDL_Rect; var C: TSDL_Color; MyFont: PTTF_FONT ); @@ -247,9 +280,20 @@ implementation const WindowName: PChar = 'GearHead Arena SDL Version'; IconName: PChar = 'GearHead'; + {$IFDEF JOYSTICK_SUPPORT} + JOY_DirButtons = [BUTTON_UP, BUTTON_DOWN, BUTTON_LEFT, BUTTON_RIGHT]; + {$ENDIF} var Infobox_Border,Infobox_Backdrop: SensibleSpritePtr; + {$IFDEF JOYSTICK_SUPPORT} + Joystick: PSDL_Joystick; + PrevState: TButtonSet; + PrevTicks: LongWord = 0; + PrevHasRepeated: Boolean = false; + JoyXAxis, JoyYAxis: SmallInt; + {$ENDIF} + Key_Font: PTTF_Font; Function DynamicRect.GetRect: TSDL_Rect; { Return the TSDL_Rect described by this DynamicRect, given the current } @@ -641,15 +685,38 @@ procedure DrawAlphaSprite( Spr: SensibleSpritePtr; MyDest: TSDL_Rect; Frame: Int ConfirmSprite := S; end; +{$IFDEF JOYSTICK_SUPPORT} +Function GetJoyAxisDir(): TButtonSet; +{get the direction the control stick is aimed in} +begin + GetJoyAxisDir := []; + + if JoyYAxis < -JOY_AxisCutoff then Include(GetJoyAxisDir, BUTTON_UP) + else if JoyYAxis > JOY_AxisCutoff then Include(GetJoyAxisDir, BUTTON_DOWN); + if JoyXAxis < -JOY_AxisCutoff then Include(GetJoyAxisDir, BUTTON_LEFT) + else if JoyXAxis > JOY_AxisCutoff then Include(GetJoyAxisDir, BUTTON_RIGHT); +end; +{$ENDIF} + +function RPGKey: Char inline; + { Read a readable key from the keyboard and return its ASCII value. } +begin + RPGKey := RPGKey(CONTEXT_NONE); +end; -function RPGKey: Char; +function RPGKey(context: TInputContext): Char; { Read a readable key from the keyboard and return its ASCII value. } + { Context is only used for gamepad inputs. } var a: String; event : TSDL_Event; m2: PChar; - width,height: Integer; pmsg: PChar; + {$IFDEF JOYSTICK_SUPPORT} + direction: TButtonSet; + adjustedButtons: TButtonSet; + button: TJoyButton; + {$ENDIF} begin a := ''; repeat @@ -690,20 +757,124 @@ function RPGKey: Char; end; end else if event.type_ = SDL_VIDEORESIZE then begin - width := event.resize.w; - if width < 800 then width := 800; - height := event.resize.h; - if height < 600 then height := 600; - Game_Screen := SDL_SetVideoMode(width, height, 0, SDL_HWSURFACE or SDL_DoubleBuf or SDL_RESIZABLE ); + ResizeScreen(event.resize.w, event.resize.h); + + {$IFDEF JOYSTICK_SUPPORT} + end else if event.type_ = SDL_JOYBUTTONDOWN then begin + Include(ButtonState, FindButton(event.jbutton.button)); + end else if event.type_ = SDL_JOYBUTTONUP then begin + Exclude(ButtonState, FindButton(event.jbutton.button)); + end else if event.type_ = SDL_JOYAXISMOTION then begin + if event.jaxis.axis = JoyXIndex then JoyXAxis := event.jaxis.value + else if event.jaxis.axis = JoyYIndex then JoyYAxis := event.jaxis.value + else begin + ButtonState -= FindButtonSet(event.jaxis.axis, TYPE_AXIS); + if abs(event.jaxis.value) > JOY_AxisCutoff then + Include(ButtonState, FindButton(event.jaxis.axis, TYPE_AXIS, Sgn(event.jaxis.value))); + end; + JoyAxisDir := GetJoyAxisDir; + end else if event.type_ = SDL_JOYHATMOTION then begin + ButtonState -= FindButtonSet(event.jhat.hat, TYPE_HAT); + + if (event.jhat.value and SDL_HAT_UP) <> 0 then + Include(ButtonState, FindButton(event.jhat.hat, TYPE_HAT, SDL_HAT_UP)); + if (event.jhat.value and SDL_HAT_DOWN) <> 0 then + Include(ButtonState, FindButton(event.jhat.hat, TYPE_HAT, SDL_HAT_DOWN)); + if (event.jhat.value and SDL_HAT_LEFT) <> 0 then + Include(ButtonState, FindButton(event.jhat.hat, TYPE_HAT, SDL_HAT_LEFT)); + if (event.jhat.value and SDL_HAT_RIGHT) <> 0 then + Include(ButtonState, FindButton(event.jhat.hat, TYPE_HAT, SDL_HAT_RIGHT)); + {$ENDIF} end; end else begin - if SDL_GetTicks < ( Last_Clock_Update + 20 ) then SDL_Delay( Last_Clock_Update + 30 - SDL_GetTicks ); - Last_Clock_Update := SDL_GetTicks + 30; - Animation_Phase := ( Animation_Phase + 1 ) mod 6000; - a := RPK_TimeEvent; + {$IFDEF JOYSTICK_SUPPORT} + {special handling around the a button and the joystick} + {on menus/lookers we just let the joystick work, otherwise it only counts if you hit a} + if (JoyAxisDir <> []) and ((context <> CONTEXT_NONE) or (BUTTON_A in ButtonState)) then direction := JoyAxisDir + else direction := ButtonState * JOY_DirButtons; + + {mask out the direction from the main state, in case we're using the joystick instead} + adjustedButtons := (ButtonState - JOY_DirButtons) + direction; + + {faked button repeat} + if PrevState <> adjustedButtons then begin + PrevState := adjustedButtons; + PrevTicks := SDL_GetTicks; + PrevHasRepeated := false; + end else begin + if not PrevHasRepeated then begin + if SDL_GetTicks - PrevTicks <= (GH_REPEAT_DELAY * 2) then adjustedButtons := [] + else begin + PrevTicks := SDL_GetTicks; + PrevHasRepeated := true; + end; + end else begin + if SDL_GetTicks - PrevTicks <= GH_REPEAT_INTERVAL then adjustedButtons := [] + else PrevTicks := SDL_GetTicks; + end; + end; + + if adjustedButtons <> [] then begin + {Use mapped inputs for non-menus only} + {thanks, pascal, for not supporting sets in case statements} + case context of + CONTEXT_NONE, CONTEXT_LOOKER: begin + if direction = [] then a := '' + else if direction = [BUTTON_UP] then a := KeyMap[KMC_NorthWest].KCode + else if direction = [BUTTON_DOWN] then a := KeyMap[KMC_SouthEast].KCode + else if direction = [BUTTON_LEFT] then a := KeyMap[KMC_SouthWest].KCode + else if direction = [BUTTON_RIGHT] then a := KeyMap[KMC_NorthEast].KCode + else if direction = [BUTTON_UP, BUTTON_LEFT] then a := KeyMap[KMC_West].KCode + else if direction = [BUTTON_UP, BUTTON_RIGHT] then a := KeyMap[KMC_North].KCode + else if direction = [BUTTON_DOWN, BUTTON_LEFT] then a := KeyMap[KMC_South].KCode + else if direction = [BUTTON_DOWN, BUTTON_RIGHT] then a := KeyMap[KMC_East].KCode; + end; + CONTEXT_MENU: begin + if direction = [] then a := '' + else if direction = [BUTTON_UP] then a := RPK_Up + else if direction = [BUTTON_DOWN] then a := RPK_Down + else if direction = [BUTTON_LEFT] then a := RPK_Left + else if direction = [BUTTON_RIGHT] then a := RPK_Right + else if direction = [BUTTON_UP, BUTTON_LEFT] then a := RPK_UpLeft + else if direction = [BUTTON_UP, BUTTON_RIGHT] then a := RPK_UpRight + else if direction = [BUTTON_DOWN, BUTTON_LEFT] then a := RPK_DownLeft + else if direction = [BUTTON_DOWN, BUTTON_RIGHT] then a := RPK_DownRight; + end; + end; + + {directions take precedence over actions on the field} + {otherwise actions take precedence} + if (a = '') or (context <> CONTEXT_NONE) then begin + {handle non-directional buttons} + case context of + CONTEXT_NONE: begin + {thanks to directions overriding other inputs, we don't need to special case the a button} + for button := BUTTON_A to BUTTON_OTHER4 do begin + if button in adjustedButtons then begin + if ButtonMap[ord(button)].MappedCmd <> NIL then a := ButtonMap[ord(button)].MappedCmd^.KCode; + break; + end; + end; + end; + CONTEXT_MENU, CONTEXT_LOOKER: begin + if BUTTON_A in adjustedButtons then a:= #10 + else if BUTTON_B in adjustedButtons then a:= #8 + else if BUTTON_L in adjustedButtons then a:= '/' + else if BUTTON_R in adjustedButtons then a:= '/' + else if BUTTON_START in adjustedButtons then a:= #27; + end; + end; + end; + end; + {$ENDIF} + + if a = '' then begin + CheckAnimTicks; + a := RPK_TimeEvent; + end; end; { Keep going until either a character is found, or an error is reported. } @@ -734,6 +905,25 @@ function RPGKey: Char; SDL_FillRect( game_screen , Nil , SDL_MapRGB( Game_Screen^.Format , 0 , 0 , 0 ) ); end; +Procedure ResizeScreen(width, height: Integer) inline; + { resize screen to specified dimensions} +var + flags: LongWord; +begin + flags := SDL_GHABASE; + if DoFullScreen then flags := flags or SDL_FULLSCREEN else flags := flags or SDL_RESIZABLE; + if width < 800 then width := 800; + if height < 600 then height := 600; + Game_Screen := SDL_SetVideoMode(width, height, 0, flags); +end; + +Procedure CheckAnimTicks() inline; +begin + if SDL_GetTicks < ( Last_Clock_Update + 20 ) then SDL_Delay( Last_Clock_Update + 30 - SDL_GetTicks ); + Last_Clock_Update := SDL_GetTicks + 30; + Animation_Phase := ( Animation_Phase + 1 ) mod 6000; +end; + Function TextLength( F: PTTF_Font; const msg: String ): LongInt; { Determine how long "msg" will be using the default "game_font". } var @@ -786,10 +976,8 @@ function RPGKey: Char; { in lines of no longer than "width" pixels. Sound simple? Mostly just } { tedious, I'm afraid. } var - SList,SA: SAttPtr; - S_Total,S_Temp: PSDL_Surface; - MyDest: SDL_Rect; - pline: PChar; + SList: SAttPtr; + S_Total: PSDL_Surface; NextWord: String; THELine: String; {The line under construction.} begin @@ -814,16 +1002,31 @@ function RPGKey: Char; TheLine := NextWord; end; { while TheLine <> '' } + S_Total := BlockPrint(SList, Width, FG, DoCenter, MyFont); + + DisposeSAtt(SList); + + PrettyPrint := S_Total; +end; + +Function BlockPrint( const msg: SAttPtr; Width: Integer; var FG: TSDL_Color; DoCenter: Boolean; MyFont: PTTF_Font ): PSDL_Surface; + { Create a SDL_Surface containing all the text within "msg". } + { No formatting, just line-by-line } +var + SA: SAttPtr; + S_Total,S_Temp: PSDL_Surface; + MyDest: SDL_Rect; + pline: PChar; +begin { Create a bitmap for the message. } - if SList <> Nil then begin + if msg <> Nil then begin { Create a big bitmap to hold everything. } -{ S_Total := SDL_CreateRGBSurface( SDL_SWSURFACE , width , TTF_FontLineSkip( MyFont ) * NumSAtts( SList ) , 16 , 0 , 0 , 0 , 0 ); -} S_Total := SDL_CreateRGBSurface( SDL_SWSURFACE , width , TTF_FontLineSkip( MyFont ) * NumSAtts( SList ) , 32 , $FF000000 , $00FF0000 , $0000FF00 , $000000FF ); + S_Total := SDL_CreateRGBSurface( SDL_SWSURFACE , width , TTF_FontLineSkip( MyFont ) * NumSAtts( msg ) , 32 , $FF000000 , $00FF0000 , $0000FF00 , $000000FF ); MyDest.X := 0; MyDest.Y := 0; { Add each stored string to the bitmap. } - SA := SList; + SA := msg; while SA <> Nil do begin pline := QuickPCopy( SA^.Info ); S_Temp := TTF_RenderText_Solid( MyFont , pline , fg ); @@ -845,14 +1048,13 @@ function RPGKey: Char; MyDest.Y := MyDest.Y + TTF_FontLineSkip( MyFont ); SA := SA^.Next; end; - DisposeSAtt( SList ); end else begin S_Total := Nil; end; - PrettyPrint := S_Total; + BlockPrint := S_Total; end; Procedure QuickText( const msg: String; MyDest: TSDL_Rect; Color: TSDL_Color ); @@ -881,6 +1083,9 @@ function RPGKey: Char; begin pline := QuickPCopy( msg ); MyText := TTF_RenderText_Solid( info_font , pline , Color ); + {$IFDEF LINUX} + SDL_SetColorKey( MyText , SDL_SRCCOLORKEY , SDL_MapRGB( MyText^.Format , 0 , 0, 0 ) ); + {$ENDIF} Dispose( pline ); MyDest.X := MyDest.X - ( MyText^.W div 2 ); SDL_BlitSurface( MyText , Nil , Game_Screen , @MyDest ); @@ -905,6 +1110,24 @@ function RPGKey: Char; end; end; +Procedure BMessage( const msg: SAttPtr; Z: TSDL_Rect; C: TSDL_Color; font: PTTF_Font ); + { Print a list of strings to the screen, centered in the requested rect. } + { Clear the specified zone before doing so. } + { Does not do any processing on the strings. } +var + MyText: PSDL_Surface; + MyDest: TSDL_Rect; +begin + MyText := BlockPrint( msg , Z.W , C , True, font ); + if MyText <> Nil then begin + MyDest := Z; + MyDest.Y := MyDest.Y + ( Z.H - MyText^.H ) div 2; + SDL_SetClipRect( Game_Screen , @Z ); + SDL_BlitSurface( MyText , Nil , Game_Screen , @MyDest ); + SDL_FreeSurface( MyText ); + SDL_SetClipRect( Game_Screen , Nil ); + end; +end; Procedure GameMSG( const msg: string; Z: TSDL_Rect; var C: TSDL_Color; MyFont: PTTF_FONT ); { Print a line-justified message in the requested screen zone. } @@ -974,7 +1197,7 @@ function RPGKey: Char; begin { Keep reading keypresses until either a space or an ESC/Backspace is found. } repeat - A := RPGKey; + A := RPGKey(CONTEXT_MENU); until ( A = ' ' ) or ( A = #27 ) or ( A = #8 ); end; @@ -1161,37 +1384,99 @@ function RPGKey: Char; const AllowableCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890()-=_+,.?"*'; MaxInputLength = 80; + AllKeys: array[0..4,0..14] of Char = (('1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '+', '-', '=', '?', '!'), + ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', '"'), + ('O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '(', ')', ','), + ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', '.'), + ('o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ' ', '*', '#')); var A: Char; - it: String; - MyBigBox,MyInputBox,MyDest: TSDL_Rect; + it: String[MaxInputLength]; + MyBigBox,MyInputBox,MyDest,KeyboardRect: TSDL_Rect; + xPos, yPos, i, j: Integer; + keyLine: String[45]; + keyboard: SAttPtr; begin { Initialize string. } it := ''; + xPos := 0; + yPos := 0; repeat MyBigBox := ZONE_TextInputBigBox.GetRect(); MyInputBox := ZONE_TextInput.GetRect(); + KeyboardRect := ZONE_KeyboardInput.GetRect(); { Set up the display. } if ReDrawer <> Nil then ReDrawer; InfoBox( MyBigBox ); + {SDL_FillRect( game_screen , @MyBigBox , SDL_MapRGB( Game_Screen^.Format , BorderBlue.R , BorderBlue.G , BorderBlue.B ) );} SDL_FillRect( game_screen , @MyInputBox , SDL_MapRGB( Game_Screen^.Format , StdBlack.R , StdBlack.G , StdBlack.B ) ); + if OnScreenKeyboard then begin + InfoBox( KeyboardRect ); + SDL_FillRect( game_screen , @KeyboardRect , SDL_MapRGB( Game_Screen^.Format , StdBlack.R , StdBlack.G , StdBlack.B ) ); + end; + CMessage( Prompt , ZONE_TextInputPrompt.GetRect() , StdWhite ); CMessage( it , MyInputBox , InfoGreen ); MyDest.Y := MyInputBox.Y + 2; MyDest.X := MyInputBox.X + ( MyInputBox.W div 2 ) + ( TextLength( Game_Font , it ) div 2 ); DrawSprite( Cursor_Sprite , MyDest , ( Animation_Phase div 2 ) mod 4 ); + if OnScreenKeyboard then begin + {Build keyboard strings} + keyboard := nil; + + for i := 0 to Length(AllKeys) - 1 do begin + keyLine := ''; + + for j := 0 to Length(AllKeys[0]) - 1 do begin + if (i = yPos) and (j = xPos) then begin + keyLine := keyLine + ' >' + AllKeys[i, j]; + end else begin + keyLine := keyLine + ' ' + AllKeys[i, j]; + end; + end; + + StoreSAtt(keyboard, keyLine); + end; + + BMessage(keyboard, KeyboardRect, StdWhite, Key_Font); + + DisposeSAtt(keyboard); + end; + GHFlip; - A := RPGKey; + A := RPGKey(CONTEXT_MENU); + + + if not OnScreenKeyboard then begin + if ( A = #8 ) and ( Length( it ) > 0 ) then begin + it := Copy( it , 1 , Length( it ) - 1 ); + end else if ( Pos( A , AllowableCharacters ) > 0 ) and ( Length( it ) < MaxInputLength ) then begin + it := it + A; + end; + end else begin + case A of + RPK_Up: yPos := yPos - 1; + RPK_Down: yPos := (yPos + 1) mod Length(AllKeys); + RPK_Left: xPos := xPos - 1; + RPK_Right: xPos := (xPos + 1) mod Length(AllKeys[0]); + #10: begin + if (AllKeys[yPos, xPos] <> '#') and (Length(it) < MaxInputLength) then begin + it := it + AllKeys[yPos, xPos]; + end else begin + A := #13; + end; + end; + #8: if Length( it ) > 0 then it := Copy( it , 1 , Length( it ) - 1 ); + end; - if ( A = #8 ) and ( Length( it ) > 0 ) then begin - it := Copy( it , 1 , Length( it ) - 1 ); - end else if ( Pos( A , AllowableCharacters ) > 0 ) and ( Length( it ) < MaxInputLength ) then begin - it := it + A; + {bracket selected position} + if (yPos < 0) then yPos := Length(AllKeys) - 1; + if (xPos < 0) then xPos := Length(AllKeys[0]) - 1; end; until ( A = #13 ) or ( A = #27 ); @@ -1275,7 +1560,7 @@ function RPGKey: Char; begin repeat { Get input from user. } - A := RPGKey; + A := RPGKey(CONTEXT_MENU); { Possibly process this input. } if A = RPK_Down then begin @@ -1463,23 +1748,75 @@ function RPGKey: Char; } end; +function GetAvailableModes(): TSDLModeArray; +var + modes: PPSDL_Rect; + curMode: PSDL_Rect; + i, count: Integer; +begin + i := 0; + count := 1; + modes := SDL_ListModes(NIL, SDL_GHABASE or SDL_FULLSCREEN); + + SetLength(GetAvailableModes, count); + GetAvailableModes[0].Name := BStr(ScreenWidth) + 'x' + BStr(ScreenHeight); + GetAvailableModes[0].Width := ScreenWidth; + GetAvailableModes[0].Height := ScreenHeight; + + if (modes <> NIL) and (modes <> PPSDL_Rect(-1)) then begin + while modes[i] <> NIL do begin + curMode := modes[i]; + i += 1; + if (curMode^.w > ScreenWidth) or (curMode^.h > ScreenHeight) then begin + count += 1; + SetLength(GetAvailableModes, count); + GetAvailableModes[count - 1].Name := BStr(curMode^.w) + 'x' + BStr(curMode^.h); + GetAvailableModes[count - 1].Width := curMode^.w; + GetAvailableModes[count - 1].Height := curMode^.h; + end; + end; + end; +end; initialization - SDL_Init( SDL_INIT_VIDEO or SDL_INIT_AUDIO ); + {$IFDEF JOYSTICK_SUPPORT} + SDL_Init( SDL_INIT_VIDEO or SDL_INIT_JOYSTICK ); + {$ELSE} + SDL_Init( SDL_INIT_VIDEO ); + {$ENDIF} + + AvailableModes := GetAvailableModes; if DoFullScreen then begin - Game_Screen := SDL_SetVideoMode(ScreenWidth, ScreenHeight, 0, SDL_HWSURFACE or SDL_FULLSCREEN or SDL_DoubleBuf ); - Mouse_Pointer := IMG_Load( Graphics_Directory + 'cosplay_pointer.png' ); - SDL_SetColorKey( Mouse_Pointer , SDL_SRCCOLORKEY or SDL_RLEACCEL , SDL_MapRGB( Mouse_Pointer^.Format , 0 , 0, 255 ) ); + if ModeIndex in [0 .. high(AvailableModes)] then + Game_Screen := SDL_SetVideoMode(AvailableModes[ModeIndex].Width, AvailableModes[ModeIndex].Height, 0, SDL_GHABASE or SDL_FULLSCREEN) + else Game_Screen := SDL_SetVideoMode(ScreenWidth, ScreenHeight, 0, SDL_GHABASE or SDL_FULLSCREEN); + + if Mouse_Active then begin + Mouse_Pointer := IMG_Load( Graphics_Directory + 'cosplay_pointer.png' ); + SDL_SetColorKey( Mouse_Pointer , SDL_SRCCOLORKEY or SDL_RLEACCEL , SDL_MapRGB( Mouse_Pointer^.Format , 0 , 0, 255 ) ); + end; end else begin - Game_Screen := SDL_SetVideoMode(ScreenWidth, ScreenHeight, 0, SDL_HWSURFACE or SDL_DoubleBuf or SDL_RESIZABLE ); + if ModeIndex in [0 .. high(AvailableModes)] then + Game_Screen := SDL_SetVideoMode(AvailableModes[ModeIndex].Width, AvailableModes[ModeIndex].Height, 0, SDL_GHABASE or SDL_RESIZABLE) + else Game_Screen := SDL_SetVideoMode(ScreenWidth, ScreenHeight, 0, SDL_GHABASE or SDL_RESIZABLE); Mouse_Pointer := Nil; end; SDL_EnableUNICODE( 1 ); SDL_EnableKeyRepeat( GH_REPEAT_DELAY , GH_REPEAT_INTERVAL ); + {if there's no cursor or we have our own cursor, just hide the system cursor} + if (not Mouse_Active) or DoFullScreen then SDL_ShowCursor(SDL_DISABLE); + + {$IFDEF JOYSTICK_SUPPORT} + Joystick := SDL_JoystickOpen(0); + HasJoystick := Joystick <> NIL; + if HasJoystick then ZONE_TitleScreenMenu.h := 113; + {$ENDIF} + + if OnScreenKeyboard then ZONE_PhoneInstructions.dy := -95; Game_Sprites := Nil; @@ -1488,6 +1825,7 @@ initialization TTF_Init; Game_Font := TTF_OpenFont( 'Image' + OS_Dir_Separator + 'VeraBd.ttf' , BigFontSize ); Info_Font := TTF_OpenFont( 'Image' + OS_Dir_Separator + 'VeraMoBd.ttf' , SmallFontSize ); + Key_Font := TTF_OpenFont( 'Image' + OS_Dir_Separator + 'VeraMono.ttf' , BigFontSize ); Text_Messages := LoadStringList( Standard_Message_File ); Console_History := Nil; @@ -1517,11 +1855,14 @@ finalization DisposeSpriteList( Game_Sprites ); TTF_CloseFont( Game_Font ); TTF_CloseFont( Info_Font ); + TTF_CloseFont( Key_Font ); TTF_Quit; if Mouse_Pointer <> Nil then SDL_FreeSurface( Mouse_Pointer ); - SDL_FreeSurface( Game_Screen ); + {$IFDEF JOYSTICK_SUPPORT} + if Joystick <> Nil then SDL_JoystickClose( Joystick ); + {$ENDIF} SDL_Quit; DisposeSAtt( Text_Messages ); diff --git a/sdlinfo.pp b/sdlinfo.pp index 5377de0..97095cd 100644 --- a/sdlinfo.pp +++ b/sdlinfo.pp @@ -1172,7 +1172,7 @@ implementation A: Char; begin repeat - A := RPGKey; + A := RPGKey(CONTEXT_MENU); if A = RPK_TimeEvent then begin if Redrawer <> Nil then Redrawer(); RealInjuryDisplay; diff --git a/sdlmap.pp b/sdlmap.pp index c67e88a..e639f62 100644 --- a/sdlmap.pp +++ b/sdlmap.pp @@ -54,6 +54,12 @@ procedure IndicateTile( GB: GameBoardPtr; X , Y , Z: Integer; Primary: Boolean ) procedure IndicateTile( GB: GameBoardPtr; Mek: GearPtr; Primary: Boolean ); procedure IndicateTile( GB: GameBoardPtr; X,Y: Integer ); Procedure MouseAtTile( GB: GameBoardPtr; X,Y: Integer ); +{$IFDEF JOYSTICK_SUPPORT} +procedure JoystickIndicate(const Mek: GearPtr); +{$ENDIF} +{$IFDEF SDLMODE} +procedure ClearOverlay; +{$ENDIF} Procedure RevealMek( GB: GameBoardPtr; Mek,Spotter: GearPtr ); Procedure VisionCheck( GB: GameBoardPtr; Mek: GearPtr ); @@ -81,6 +87,10 @@ procedure IndicateTile( GB: GameBoardPtr; X,Y: Integer ); Function ScreenToMap( X,Y: Integer ): Point; Function MouseMapPos: Point; +{$IFDEF JOYSTICK_SUPPORT} +Function JoystickIndicatorPos(M: GearPtr): Point; +Procedure JoystickIndicatorForRedraw(); +{$ENDIF} Procedure BeginTurn( GB: GameBoardPtr; M: GearPtr ); @@ -432,7 +442,7 @@ implementation end else begin DrawSprite( OVERLAY_MAP[ X ,Y , Z , T ].Sprite , MyDest , OVERLAY_MAP[ X ,Y , Z , T ].F ); end; - if ( OVERLAY_MAP[ X ,Y , Z , T ].name <> '' ) and NAMES_ABOVE_HEADS then begin + if NAMES_ABOVE_HEADS and ( OVERLAY_MAP[ X ,Y , Z , T ].name <> '' ) then begin MyDest.X := ScreenX( X , Y ) + HalfTileWidth; MyDest.Y := ScreenY( X , Y ) - Altitude_Height * Z - 10; QuickTinyText( OVERLAY_MAP[ X ,Y , Z , T ].name , MyDest , StdWhite ); @@ -852,14 +862,41 @@ procedure IndicateTile( GB: GameBoardPtr; X,Y: Integer ); Procedure MouseAtTile( GB: GameBoardPtr; X,Y: Integer ); { The mouse is apparently hovering over this tile. Draw the mouse cursor here. } begin - ClearOverlayLayer( OVERLAY_IMAGE ); if OnTheMap( X , Y ) then begin Overlay_MAP[ X , Y , 0 , OVERLAY_IMAGE ].Sprite := Targeting_Srpite; Overlay_MAP[ X , Y , 0 , OVERLAY_IMAGE ].F := 1; + end; +end; + +{$IFDEF JOYSTICK_SUPPORT} +procedure JoystickIndicate(const Mek: GearPtr); +var + P: Point; +begin + if JoyAxisDir <> [] then begin + P := JoystickIndicatorPos(Mek); + if OnTheMap( P.X , P.Y ) then begin + Overlay_MAP[ P.X , P.Y , 0 , OVERLAY_IMAGE ].Sprite := Targeting_Srpite; + Overlay_MAP[ P.X , P.Y , 0 , OVERLAY_IMAGE ].F := 0; + end; end; end; +Procedure JoystickIndicatorForRedraw(); +begin + ClearOverlay; + if ComDisplay_PC <> NIL then JoystickIndicate(ComDisplay_PC); +end; + +{$ENDIF} + +{$IFDEF SDLMODE} +procedure ClearOverlay; +begin + ClearOverlayLayer( OVERLAY_IMAGE ); +end; +{$ENDIF} Procedure RevealMek( GB: GameBoardPtr; Mek,Spotter: GearPtr ); { This mek has been spotted. Light it up. } @@ -1558,6 +1595,37 @@ procedure IndicateTile( GB: GameBoardPtr; X,Y: Integer ); MouseMapPos := ScreenToMap( Mouse_X , Mouse_Y ); end; +{$IFDEF JOYSTICK_SUPPORT} +Function JoystickIndicatorPos(M: GearPtr): Point; + { Return the position the indicator should be drawn at, relative to the gear} +var + P: Point; +begin + P.X := NAttValue(M^.NA, NAG_Location, NAS_X); + P.Y := NAttValue(M^.NA, NAG_Location, NAS_Y); + + {my kingdom for a working case statement} + if JoyAxisDir = [BUTTON_UP] then begin + P.Y -= 1; + P.X -= 1; + end else if JoyAxisDir = [BUTTON_DOWN] then begin + P.Y += 1; + P.X += 1; + end else if JoyAxisDir = [BUTTON_LEFT] then begin + P.Y += 1; + P.X -= 1; + end else if JoyAxisDir = [BUTTON_RIGHT] then begin + P.Y -= 1; + P.X += 1; + end else if JoyAxisDir = [BUTTON_UP, BUTTON_LEFT] then P.X -= 1 + else if JoyAxisDir = [BUTTON_UP, BUTTON_RIGHT] then P.Y -= 1 + else if JoyAxisDir = [BUTTON_DOWN, BUTTON_LEFT] then P.Y += 1 + else if JoyAxisDir = [BUTTON_DOWN, BUTTON_RIGHT] then P.X += 1; + + JoystickIndicatorPos := P; +end; +{$ENDIF} + Procedure BeginTurn( GB: GameBoardPtr; M: GearPtr ); { Time to start the turn. } var diff --git a/sdlmenus.pp b/sdlmenus.pp index 9984f38..084005f 100644 --- a/sdlmenus.pp +++ b/sdlmenus.pp @@ -521,7 +521,7 @@ implementation {or cancels the menu using the ESC key.} repeat {Read the input from the keyboard.} - getit := RPGKey; + getit := RPGKey(CONTEXT_MENU); {Certain keys need processing- if so, process them.} case getit of diff --git a/texutil.pp b/texutil.pp index a061431..a2b5cd2 100644 --- a/texutil.pp +++ b/texutil.pp @@ -52,6 +52,7 @@ interface Function IsPunctuation( C: Char ): Boolean; Procedure ReplacePat( var msg: String; const pat_in,s: String ); +Function Replace(constref msg, pat_in, s: String): String; Function ReplaceHash( const msg, s: String ): String; Procedure SanitizeFilename( var S: String ); @@ -242,8 +243,7 @@ implementation function IsAlpha( C: Char ): Boolean; { Return TRUE if C is a letter, FALSE otherwise. } begin - if ( UpCase( C ) >= 'A' ) and ( UpCase( C ) <= 'Z' ) then IsAlpha := True - else IsAlpha := False; + IsAlpha := UpCase(C) in ['A'..'Z']; end; Function Acronym( phrase: String ): String; {can't const} @@ -359,10 +359,7 @@ function IsAlpha( C: Char ): Boolean; { forgive me if my definition of punctuation in this function } { is not the same as my own. } begin - case C of - '.',',',':',';','@','!','/','?','''': IsPunctuation := True; - else IsPunctuation := False; - end; + IsPunctuation := C in ['.',',',':',';','@','!','/','?','''']; end; Procedure ReplacePat( var msg: String; const pat_in,s: String ); @@ -380,6 +377,13 @@ function IsAlpha( C: Char ): Boolean; until N = 0; end; +Function Replace(constref msg, pat_in, s: String): String; + { Replace a pattern without altering the original string} +begin + Replace := Copy(msg, 0, Length(msg)); + ReplacePat(Replace, pat_in, s); +end; + Function ReplaceHash( const msg,s: String ): String; { Look for a hash sign in MSG. Replace it with S. } var @@ -398,14 +402,12 @@ function IsAlpha( C: Char ): Boolean; Procedure SanitizeFilename( var S: String ); { Replace all proscribed characters with an underscore. } const - ProscribedCharacters = ',?"*~#%&{}:<>+|'; + ProscribedCharacters = [',', '?', '"', '*', '~', '#', '%', '&', '{', '}', ':', '<', '>', '+', '|', '''']; var T: Integer; begin for T := 1 to Length( S ) do begin - if Pos( S[T] , ProscribedCharacters ) > 0 then begin - S[T] := '_'; - end; + if S[T] in ProscribedCharacters then S[T] := '_'; end; end; diff --git a/ui4gh.pp b/ui4gh.pp index cd13830..31ea97f 100644 --- a/ui4gh.pp +++ b/ui4gh.pp @@ -32,6 +32,38 @@ interface KCode: Char; end; + {$IFDEF JOYSTICK_SUPPORT} + {$PACKENUM 1} + TJoyButton = (BUTTON_A, + BUTTON_B, + BUTTON_X, + BUTTON_Y, + BUTTON_START, + BUTTON_SELECT, + BUTTON_L, + BUTTON_R, + BUTTON_OTHER1, + BUTTON_OTHER2, + BUTTON_OTHER3, + BUTTON_OTHER4, + BUTTON_UP, + BUTTON_DOWN, + BUTTON_LEFT, + BUTTON_RIGHT, + BUTTON_NONE); + TButtonType = (TYPE_BUTTON, TYPE_HAT, TYPE_AXIS, TYPE_NONE); + + TButtonSet = set of TJoyButton; + + TButtonMapDesc = packed record + ConfigName: String[12]; {Name for config file} + BCode: ShortInt; {Code for SDL} + BType: TButtonType; {What event to listen to} + BDir: ShortInt; {Which direction (for hat and axis)} + MappedCmd: ^KeyMapDesc; {What action this is bound to (directions work differently)} + end; + {$ENDIF} + const RPK_UpRight = '9'; RPK_Up = '8'; @@ -47,6 +79,10 @@ interface RPK_TimeEvent = #$91; RPK_RightButton = #$92; FrameDelay: Integer = 50; + {$IFDEF JOYSTICK_SUPPORT} + JoyXIndex: SmallInt = 0; + JoyYIndex: SmallInt = 1; + {$ENDIF} {$ELSE} FrameDelay: Integer = 100; {$ENDIF} @@ -95,6 +131,10 @@ interface UseAdvancedColoring: Boolean = False; + ModeIndex: Byte = 0; + + OnScreenKeyboard: Boolean = False; + Accessibility_On: Boolean = False; { *** SCREEN DIMENSIONS *** } @@ -332,6 +372,96 @@ interface KMC_SwitchTarget = 44; KMC_RunToggle = 45; + {$IFDEF JOYSTICK_SUPPORT} + ButtonMap: array [ord(BUTTON_A)..ord(BUTTON_RIGHT)] of TButtonMapDesc = + ((ConfigName: 'ButtonA'; + BCode:0; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_Enter];), + (ConfigName: 'ButtonB'; + BCode:1; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_ApplySkill];), + (ConfigName: 'ButtonX'; + BCode:3; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_Search];), + (ConfigName: 'ButtonY'; + BCode:2; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_AttackMenu];), + (ConfigName: 'ButtonStart'; + BCode:9; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_CharInfo];), + (ConfigName: 'ButtonSelect'; + BCode:8; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_QuitGame];), + (ConfigName: 'ButtonL'; + BCode:4; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_Talk];), + (ConfigName: 'ButtonR'; + BCode:5; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_Get];), + (ConfigName: 'ButtonOther1'; + BCode:-1; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_ViewMemo];), + (ConfigName: 'ButtonOther2'; + BCode:-1; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_Telephone];), + (ConfigName: 'ButtonOther3'; + BCode:-1; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_Stop];), + (ConfigName: 'ButtonOther4'; + BCode:-1; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: @KeyMap[KMC_Rest];), + (ConfigName: 'ButtonUp'; + BCode:13; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: NIL;), + (ConfigName: 'ButtonDown'; + BCode:14; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: NIL;), + (ConfigName: 'ButtonLeft'; + BCode:15; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: NIL;), + (ConfigName: 'ButtonRight'; + BCode:16; + BType:TYPE_BUTTON; + BDir:0; + MappedCmd: NIL;)); + {$ENDIF} + +{$IFDEF JOYSTICK_SUPPORT} +Function FindButton(val: Integer) : TJoyButton; +Function FindButton(val: Integer; kind: TButtonType; dir: ShortInt) : TJoyButton; +Function FindButtonSet(val: Integer; kind: TButtonType) : TButtonSet; +{$ENDIF} + implementation uses dos,ability,gears,texutil; @@ -343,6 +473,9 @@ implementation F: Text; S,CMD,C: String; T: Integer; + {$IFDEF JOYSTICK_SUPPORT} + U: Integer; + {$ENDIF} begin {See whether or not there's a configuration file.} S := FSearch(Config_File,'.'); @@ -364,9 +497,50 @@ implementation if Length(C) = 1 then begin KeyMap[t].KCode := C[1]; end; + break; + end; + end; + + {$IFDEF JOYSTICK_SUPPORT} + {loading button mapping from the config} + for t := Low(ButtonMap) to High(ButtonMap) do begin + if UpCase(ButtonMap[t].ConfigName) = cmd then begin + C := ExtractWord(S); + + if Length(C) = 1 then begin + case C[1] of + 'H': ButtonMap[t].BType := TYPE_HAT; + 'A': ButtonMap[t].BType := TYPE_AXIS; + else + ButtonMap[t].BType := TYPE_BUTTON; + end; + end; + + ButtonMap[t].BCode := ExtractValue(S); + if ButtonMap[t].BType <> TYPE_BUTTON then ButtonMap[t].BDir := ExtractValue(S); + C := UpCase(ExtractWord(S)); + ButtonMap[t].MappedCmd := NIL; + + if C <> '' then begin + for U := Low(KeyMap) to High(KeyMap) do begin + if UpCase(KeyMap[U].CmdName) = C then begin + ButtonMap[t].MappedCmd := @KeyMap[U]; + break; + end; + end; + end; + + break; end; end; + if cmd = 'BUTTONANALOGX' then begin + JoyXIndex := ExtractValue(S); + end else if cmd = 'BUTTONANALOGY' then begin + JoyYIndex := ExtractValue(S); + end; + {$ENDIF} + { Check to see if CMD is the animation speed throttle. } if cmd = 'ANIMSPEED' then begin T := ExtractValue( S ); @@ -472,9 +646,18 @@ implementation end else if cmd = 'USETACTICSMODE' then begin UseTacticsMode := True; - end else if cmd = 'AdvancedColors' then begin + end else if cmd = 'ADVANCEDCOLORS' then begin UseAdvancedColoring := True; + end else if cmd = 'MODE' then begin + T := ExtractValue( S ); + if T > 255 then T := 255 + else if T < 0 then T := 0; + ModeIndex := T; + + end else if cmd = 'NOKEYBOARD' then begin + OnScreenKeyboard := True; + end else if cmd = 'ACCESSIBILITY_ON' then begin Accessibility_On := True; @@ -497,6 +680,9 @@ implementation var F: Text; T: Integer; + {$IFDEF JOYSTICK_SUPPORT} + S: String; + {$ENDIF} Procedure AddBoolean( const OpTag: String; IsOn: Boolean ); { Add one of the boolean options to the file. } begin @@ -508,7 +694,7 @@ implementation end; begin { If we've found a configuration file, } - { open it up and start reading. } + { open it up and start writing. } Assign( F , Config_File ); Rewrite( F ); @@ -522,6 +708,22 @@ implementation WriteLn( F, KeyMap[t].CmdName + ' ' + KeyMap[t].KCode ); end; + {$IFDEF JOYSTICK_SUPPORT} + for t := Low(ButtonMap) to High(ButtonMap) do begin + S := ButtonMap[t].ConfigName + ' '; + case ButtonMap[t].BType of + TYPE_BUTTON: S += 'B ' + BStr(ButtonMap[t].BCode); + TYPE_HAT: S += 'H ' + BStr(ButtonMap[t].BCode) + ' ' + BStr(ButtonMap[t].BDir); + TYPE_AXIS: S += 'A ' + BStr(ButtonMap[t].BCode) + ' ' + BStr(ButtonMap[t].BDir); + end; + if ButtonMap[t].MappedCmd <> NIL then S += ' ' + ButtonMap[t].MappedCmd^.CmdName; + WriteLn(F, S); + end; + + writeln(F, 'ButtonAnalogX ' + BStr(JoyXIndex)); + writeln(F, 'ButtonAnalogY ' + BStr(JoyYIndex)); + {$ENDIF} + writeln( F, 'ANIMSPEED ' + BStr( FrameDelay ) ); writeln( F, 'MECHACONTROL ' + ControlTypeName[ ControlMethod ] ); @@ -551,15 +753,55 @@ implementation AddBoolean( 'FULLSCREEN' , DoFullScreen ); AddBoolean( 'NOMOUSE' , not Mouse_Active ); + AddBoolean( 'NOKEYBOARD', OnScreenKeyboard ); AddBoolean( 'NOPILLAGE' , not Pillage_On ); AddBoolean( 'USETACTICSMODE' , UseTacticsMode ); AddBoolean( 'ADVANCEDCOLORS' , UseAdvancedColoring ); + writeln( F , 'MODE ' + BStr( ModeIndex ) ); AddBoolean( 'ACCESSIBILITY_ON' , Accessibility_On ); Close(F); end; + {$IFDEF JOYSTICK_SUPPORT} + Function FindButton(val: Integer) : TJoyButton; + { Returns the mapped button for the entered button index } + var + i: TJoyButton; + begin + for i := BUTTON_A to BUTTON_RIGHT do begin + if (ButtonMap[ord(i)].BType = TYPE_BUTTON) and (ButtonMap[ord(i)].BCode = val) then Exit(i); + end; + + FindButton := BUTTON_NONE; + end; + + Function FindButton(val: Integer; kind: TButtonType; dir: ShortInt) : TJoyButton; + {FindButton, but for hats and axis} + var + i: TJoyButton; + begin + for i := BUTTON_A to BUTTON_RIGHT do begin + if (ButtonMap[ord(i)].BType = kind) and (ButtonMap[ord(i)].BDir = dir) and (ButtonMap[ord(i)].BCode = val) then Exit(i); + end; + + FindButton := BUTTON_NONE; + end; + + Function FindButtonSet(val: Integer; kind: TButtonType) : TButtonSet; + {Return a set of all buttons associated with an index} + var + i: TJoyButton; + begin + FindButtonSet := []; + + for i := BUTTON_A to BUTTON_RIGHT do begin + if (ButtonMap[ord(i)].BType = kind) and (ButtonMap[ord(i)].BCode = val) then Include(FindButtonSet, i); + end; + end; + {$ENDIF} + initialization From 1ea50af9d0742b70858a038f95d964bc486efd2f Mon Sep 17 00:00:00 2001 From: Emong Date: Sun, 28 Jul 2019 00:13:50 -0400 Subject: [PATCH 2/2] Set defaults --- ui4gh.pp | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/ui4gh.pp b/ui4gh.pp index 31ea97f..c29dbb8 100644 --- a/ui4gh.pp +++ b/ui4gh.pp @@ -385,22 +385,22 @@ interface BDir:0; MappedCmd: @KeyMap[KMC_ApplySkill];), (ConfigName: 'ButtonX'; - BCode:3; + BCode:2; BType:TYPE_BUTTON; BDir:0; MappedCmd: @KeyMap[KMC_Search];), (ConfigName: 'ButtonY'; - BCode:2; + BCode:3; BType:TYPE_BUTTON; BDir:0; MappedCmd: @KeyMap[KMC_AttackMenu];), (ConfigName: 'ButtonStart'; - BCode:9; + BCode:7; BType:TYPE_BUTTON; BDir:0; MappedCmd: @KeyMap[KMC_CharInfo];), (ConfigName: 'ButtonSelect'; - BCode:8; + BCode:6; BType:TYPE_BUTTON; BDir:0; MappedCmd: @KeyMap[KMC_QuitGame];), @@ -415,44 +415,44 @@ interface BDir:0; MappedCmd: @KeyMap[KMC_Get];), (ConfigName: 'ButtonOther1'; - BCode:-1; - BType:TYPE_BUTTON; - BDir:0; + BCode:2; + BType:TYPE_AXIS; + BDir:1; MappedCmd: @KeyMap[KMC_ViewMemo];), (ConfigName: 'ButtonOther2'; - BCode:-1; - BType:TYPE_BUTTON; - BDir:0; + BCode:2; + BType:TYPE_AXIS; + BDir:-1; MappedCmd: @KeyMap[KMC_Telephone];), (ConfigName: 'ButtonOther3'; - BCode:-1; + BCode:8; BType:TYPE_BUTTON; BDir:0; MappedCmd: @KeyMap[KMC_Stop];), (ConfigName: 'ButtonOther4'; - BCode:-1; + BCode:9; BType:TYPE_BUTTON; BDir:0; MappedCmd: @KeyMap[KMC_Rest];), (ConfigName: 'ButtonUp'; - BCode:13; - BType:TYPE_BUTTON; - BDir:0; + BCode:0; + BType:TYPE_HAT; + BDir:1; MappedCmd: NIL;), (ConfigName: 'ButtonDown'; - BCode:14; - BType:TYPE_BUTTON; - BDir:0; + BCode:0; + BType:TYPE_HAT; + BDir:4; MappedCmd: NIL;), (ConfigName: 'ButtonLeft'; - BCode:15; - BType:TYPE_BUTTON; - BDir:0; + BCode:0; + BType:TYPE_HAT; + BDir:8; MappedCmd: NIL;), (ConfigName: 'ButtonRight'; - BCode:16; - BType:TYPE_BUTTON; - BDir:0; + BCode:0; + BType:TYPE_HAT; + BDir:2; MappedCmd: NIL;)); {$ENDIF}