Jump to content

Willicious

Members
  • Content Count

    93
  • Joined

  • Last visited

Everything posted by Willicious

  1. Willicious

    Problems with Delphi class structure / visibility

    Basically, everything I try (and fail) to do comes down to undeclared identifiers, not being able to call procedures/functions/properties/variables from other classes. It's always the thing that holds me back. All the ideas are there, and should work. Here's an example: function TReplay.GetRRChangeOnCurrentFrame: Boolean; var i: Integer; begin Result := False; for i := 0 to fSpawnIntervalChanges.Count -1 do begin if TReplayChangeSpawnInterval(fSpawnIntervalChanges[i]).Frame = LemGame.CurrentIteration then begin Result := True; OutputDebugString(PChar('SpawnIntervalChangeDetected')); Exit; end; end; end; In this code, the call for LemGame.CurrentIteration generates an undeclared identifier error. This is in spite of LemGame being one of the uses in this unit, and CurrentIteration being public and callable from other units (elsewhere, it's called as Game.CurrentIteration, but that doesn't work here either). All I want to do is return this value to True if the Spawn Interval has changed on the current frame. All the code is there, the ideas work in theory, but for some reason I just..... can't use this bit of the code in this particular place!
  2. Willicious

    Problems with Delphi class structure / visibility

    Yes, but surely we can use pseudo-code or non-compilable examples to illustrate the concepts? The only reason I'm reluctant to post actual code is because I would literally have to post multiple units-worth of code in order for anybody to be able to help. So much of what's going on is very program-specific, multi-faceted and convoluted. I've had help elsewhere on this Forum which would have worked in a newly-created codebase, for example, but which I can't do much with when I have to crowbar it in to what I'm currently dealing with. The program is a clone of the "Lemmings" game from 1991, but with modern QOL features (such as replay functionality, skill projections, etc). I'm more than happy to share the repo if anybody is interested in helping me out with it - it's an open-source project anyway. I could really do an with experienced pair of eyes and someone to help turn ideas into reality. I've already done a lot by myself, but I do continually come up against the types of problems that a seasoned Delphi expert would be able to recognise and troubleshoot quite quickly. I've even considered paying for lessons tbh, such as the difficulty of this project sometimes gets!
  3. Willicious

    Problems with Delphi class structure / visibility

    It's the idea of instances-of-classes that keeps catching me out. If a class is defined somewhere, it should be callable as that class. Why do I have to redefine/redeclare it in a different unit? Why not this instead: Unit 1 Class1 PropertyA Unit 2 uses Class1 if Class1.PropertyA etc... Anyway, never mind. I've already accepted it's a "me" problem. I seemed doomed to forever see ways in which things could be better rather than actually getting to know and use them as they are.
  4. Willicious

    Problems with Delphi class structure / visibility

    OK, I managed to get it working. I needed to declare an instance of the class in the new unit, not just declare the unit itself. For future reference, and for anyone else struggling witn this: The new instance needs to be declared at the top of the page in the current class (i.e. the one in which you want to use the instance). Like this: unit TheCurrentUnit type class TTheCurrentClass private/public (doesn't matter whether it's private or public, but public means you should in theory be able to access it elsewhere) fDeclareHereAndCallItWhateverYouWant: TTheNameOfTheOriginClass; property DeclareHereAndCallItWhateverYouWant: TReplay read fDeclareHereAndCallItWhateverYouWant; procedure TTheCurrentClass.Create; begin fDeclareHereAndCallItWhateverYouWant = TTheNameOfTheOriginClass.Create; end; procedure TTheCurrentClass.Destroy; begin fDeclareHereAndCallItWhateverYouWant.Free; end; By following the above, an instance of the class has been created in the unit which can then, in theory, be called to make use of the properties from the other unit. I'm not entirely sure why it's sometimes necessary to do this; in other examples I've seen, it's enough just to declare the unit under "implementation / uses". But, if that fails, try the example here.
  5. Willicious

    Problems with Delphi class structure / visibility

    OK, OK, I get it. I'm the problem, not the programming language. All praise the wonderful and mighty programming language that is Delphi (or, indeed, any other)! But seriously, I can spend hours on this thing and achieve so little because of problems with the code structure. I appreciate that people on here are there to help, that's a good thing. ChatGPT is also a decent tool, but it's by no means perfect. Regarding examples/context... it's difficult to provide when the context happens to be thousands and thousands of lines of code sprawling dozens of .pas unit files, none of which I created myself.
  6. Willicious

    Problems with Delphi class structure / visibility

    In fairness, I said it was a rant, not a question. I think Delphi is a very poorly structured programming language which very quickly renders programs impenetrable to anyone other than the original developer, who may even end up struggling with it if they've forgotten what is supposed to be accessible from where. Why not just have everything dynamically accessible from everywhere else? Why even the need for "private" and "public" (which, as I said in the OP, doesn't always make a difference). Yes, other languages have this going on as well but nowhere else have I struggled with it as much as with Delphi/Pascal. I'm constantly running into problems that largely involve wrestling with the structure of units & classes. Here's an example: I have a unit called "Replay.pas" which deals with saving, loading and interpreting data from text-based game replay files. Then, another unit called "ControlPanel.pas" which deals with the in-game controls and how they relate to what's happening in the game. I made a public property in the TReplay class which I need to call from ControlPanel. Even though Replay is declared in uses within ControlPanel, I can not call the property. It's brought weeks of progress to a grinding halt, and it's frustrating, hence the rant.
  7. I have a hotkey config dialog which responds to key presses by jumping to that key in a list and focusing it for editing. The user must first press "Find Key" and then select a key in order to edit it (but, I'm seriously considering removing this button altogether and just have the dialog always listen for key presses). Problem is, if they hit [Space], this clicks the "Find Key" button again. Space is focused for editing, no problem, but the "Press any key to edit..." label is shown instead of "Editing key: Space." This could cause potential confusion for the end-user. Similarly, hitting [Enter] doesn't focus Enter for editing, which is a bit frustrating but I'm not sure what I can do about that. Not looking to restructure/rewrite the unit or change the way that the app handles reading/writing key names, etc. I just want to know if there's a simple way to stop the Space bar from auto-clicking the "Find Key" button. Tried "if Key = VK_SPACE then Exit;" on the form's OnKeyDown event handler and on the [btnFindKey] OnKeyDown event handler and similar things like that, nothing seems to work. --- As an aside, if I can't find a way to get the dialog to always listen for key presses without first pressing "Find Key", then ideally it'd be better if FindKey must be pressed in order to focus any key for editing. At the moment, it's sometimes possible to press a few keys after hitting "Find Key" and each one will be focused, but then some won't be. I can't figure out what's causing this behaviour, but if there's a way to stop the form from listening for key presses once one has been focused in the list, that would also be something of an improvement.
  8. Willicious

    Prevent Space bar from focusing on a button

    I tried this. Whilst it does disable the space bar from any further presses of the "Find Key" button, it also selects key "0000" for editing in the hotkey dialog. I wonder if this can be modified to first change the key to 0 (to pull focus away from the button), but then jump to "Space" in the lvHotkeys list...?
  9. Willicious

    Prevent Space bar from focusing on a button

    In support of the above question, here are the most relevant code snippets: This is the "Find Key" button's KeyDown event handler. At present, it's necessary to have a separate TLabel for the Space bar as a band aid for the issue described in the OP. As you can see, the unit is something of a mess at the moment. There's only so much I can do without spending days rewriting the whole thing: procedure GameHotkeys.btnFindKeyKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); var i: Integer; KeyName: String; begin if Key = VK_SPACE then begin lblSpace.Visible := true; end else lblSpace.Visible := false; KeyName := fKeyNames[Key]; for i := 0 to lvHotkeys.Items.Count-1 do if KeyName = lvHotkeys.Items[i].Caption then begin lvHotkeys.SetFocus; lvHotkeys.ItemIndex := i; Exit; end; end; This is the OnClick for the FindKey button: procedure GameHotkeys.btnFindKeyClick(Sender: TObject); begin if not fShownFindInfo then begin fShownFindInfo := true; lblPressAnyKey.Visible := true; lblEditingKey.Caption := ''; end else begin lblPressAnyKey.Visible := true; lblEditingKey.Caption := ''; end; end; This is the part that sets the caption from which "Find Key" gets its signpost, but also responds to directly clicking into the lvHotkey list: procedure GameHotkeys.lvHotkeysClick(Sender: TObject); var i: Integer; begin i := FindKeyFromList(lvHotkeys.ItemIndex); if i = -1 then begin //some non-relevant stuff here lblEditingKey.Caption := ''; lblPressAnyKey.Visible := false; Exit; end; //some non-relevant stuff here lblEditingKey.Caption := 'Editing key: ' + fKeyNames[i]; lblPressAnyKey.Visible := false; lvHotkeys.Selected.MakeVisible(True); cbFunctionsChange(self); end; This is the part that finds the key from the list according to the caption: function GameHotkeys.FindKeyFromList(aValue: Integer): Integer; var i: Integer; begin Result := -1; if aValue = -1 then Exit; for i := 0 to MAX_KEY do if fKeyNames[i] = lvHotkeys.Items[aValue].Caption then begin Result := i; Exit; end; end;
  10. I need to decrement a value by 1 each time a function is called. Here's what I have so far: function TGame.HandleState: Boolean; begin Result := True; Dec(MyValue); end; Note that MyValue is a private component of the class in which this function lives, and is used by various other functions and procedures throughout the unit, so it cannot be defined within this function. The above function does indeed decrement MyValue by 1, but it does so repeatedly when the function is called only once. I need it to only decrement by 1 each time the function is called. I've tried Dec(MyValue, 1); but that just behaves the same way (i.e. repeatedly decreasing the value from a single call of the function). I also tried this: function TGame.HandleState: Boolean; var i: Integer; begin Result := True; i := MyValue -1; if i = MyValue then Dec(MyValue); end; But, that obviously doesn't work because i will never = MyValue if it's set to MyValue -1; Any ideas?
  11. Yes, this was my eventual solution! function TGame.HandleState: Boolean; begin Result := True; if Frame = 1 then Dec(MyValue); end Luckily, the animation loops from frames 8-16 once the state is entered, so all I needed to do was call Dec(MyValue); on the first frame. Pending testing, I think this one is resolved!
  12. Yes, I think the function is repeatedly "called" or "active" whilst the game is handling that particular state. So, each frame, the count is decremented by 1. That's the answer. I'm not really sure what the way around this is. Good suggestion, and it was worth a try, but sadly even the release version doesn't correct the behaviour.
  13. If you want to specify "i" as being any value between 0 - 7, you can use for i := 0 to 7 do And, as I understand it, this will run code as long as "i" matches any value between 0 and 7. But, what if you only want to run the code when "i" matches every value between 0 and 7 at the same time? So, for example, rather than typing: if (X, Y - 0) and (X, Y - 1) and (X, Y - 2) and (X, Y - 3) etc... Is there a way to express it as (X, Y - i) where i can be any value between 0 and 7, but every value must be accounted for simultaneously?
  14. Willicious

    for i := X to Y inclusive... how?

    Hmm, I can't get this to work. What is "Res :=" in this example?
  15. Willicious

    for i := X to Y inclusive... how?

    Edited
  16. Willicious

    Change "FadeOut" code to "FadeIn" code

    Forgot to check Notify again.
  17. Willicious

    Change "FadeOut" code to "FadeIn" code

    No, it's the opposite. I want to start with a black screen and finish with the full-colour screen image. The aesthetic effect would be that of the screen image fading from black. I'm more than happy to modify the codebase if there's a better way of doing things. The reason I haven't posted anything to do with how the images are currently drawn is because it's quite complicated (I barely understand it myself); the screen image is composited from a number of individual pngs which get drawn to a single bitmap (I think) - and, this happens across several units of code. I'm not entirely sure which bits are relevant. This is the Create procedure of the unit I'm currently working in, maybe this will shed some light on it?: constructor TBaseScreen.Create(aOwner: TComponent); begin inherited Create(aOwner); fScreenImg := TImage32.Create(Self); fScreenImg.Parent := Self; ScreenImg.Cursor := crNone; end; I think I've made it quite clear what I'm trying to do to be honest, I'm not sure how much clearer I can be🤷‍♂️
  18. Willicious

    for i := X to Y inclusive... how?

    The above suggestions will run the code if i is equal to any number between 1 and 7. So, it's more like this: if (i = 1) or (i = 2) or (i = 3) or (i = 4) etc... What I need is for the code to run only when all numbers are satisfied, like this (note "and" instead of "or"😞 if (i - 1) and (i = 2) and (i = 3) and (i = 4) etc... I realise that, of course, it's not possible for i to equal more than one value at once, that's why I need help with this. Essentially, it's a collision detection check in which the character has to turn around if they reach a vertical wall of at least 7 pixels in height, but - all 7 pixels have to be filled. The following code works, but I want to try and simplify it if possible: if HasPixelAt(X, Y) and HasPixelAt(X, Y -1) and HasPixelAt(X, Y -2) and HasPixelAt(X, Y -3) and HasPixelAt(X, Y -4) and HasPixelAt(X, Y -5) and HasPixelAt(X, Y -6) and HasPixelAt(X, Y -7) then begin ... end; Thanks in advance, I hope this explains the question a little better.
  19. Willicious

    Change "FadeOut" code to "FadeIn" code

    Just giving this a bump. I need it to fill the screen with a black bitmap (for which I also need control of the alpha channel). Do I need to specify a temporary bitmap image first which I then drawto, or is it possible to just fill the screen directly? procedure TGameBaseScreen.FadeIn; var A: TColor32; R: TColor32; G: TColor32; B: TColor32; i: Integer; begin for i := 0 to Width * Height - 1 do begin A := 0; R := 0; G := 0; B := 0; end; end;
  20. Willicious

    for i := X to Y inclusive... how?

    Replying so I can follow the topic
  21. I have a form with a treeview, which can be browsed using the arrow keys. Once an item in the treeview is selected, hitting an "OK" button on the form (which has the modal result mrOK) will close the form, and load the currently selected item into the app. What I want to do is assign VK_RETURN (which I believe is the [Enter] key...?) to the OK button, so that when browsing the treeview using keys, the user can just hit Enter/Return to load the currently selected item. btnOKClick has its own procedure, detailed here: procedure TFLevelSelect.btnOKClick(Sender: TObject); begin WriteToParams; ModalResult := mrOk; end; I can enter "WriteToParams;" into the Treeview's OnKeyDown procedure, but this doesn't actually have any effect until the OK button is pressed. So, one option is to tell the OnKeyDown procedure to also "virtually press the OK button" (which I'm not sure is even possible...?) - this would be my preferred method, if it can be done. Another option is to assign a hotkey to btnOKClick. The only problem with this is that there are multiple other buttons on the form, all of which already respond to [Enter] when Tab-selected. If I assign [Enter] as a hotkey to btnOKClick, then [Enter] either can't then be used for any of the other buttons on the form - or, worse, the app will crash because it won't know which button to press if the Tab is on a different button than 'OK', and the user hits [Enter]. Any and all suggestions/help welcome. I apologise if I haven't provided any necessary information.
  22. Willicious

    Change "FadeOut" code to "FadeIn" code

    I see, thanks. This helps. I'll have a look at it later today 🙂👍 I wanted to fill the screen with a black bitmap. If I can do that, then the next step is to either gradually make it more and more transparent using opacity or alpha values (thus revealing the screen image underneath), or use your example code to modulate the black pixels to those of the screen image bitmap. Whichever of these options I go with, it needs to be portable to all of the various game screens across several different units. To begin with though, I just need to draw a black bitmap. If I can do that, then I'll move on to the next step.
  23. Hi all, I'm running RAD Studio 10.4 Version 27.0.40680.4203. I daren't update it in case I can no longer compile the project I'm working on after the update. Debugging breakpoints have always worked, but today for some reason they stopped working. Now, when I enter a breakpoint (red dot at the left of the line), the red dot gets a cross ('X') in the centre of it and the program doesn't break at the appropriate point when running in Debug mode. As far as I know, I haven't changed any project settings since being able to use breakpoints and now not being able to use them. Any ideas...? Apologies if I haven't provided any necessary information.
  24. Willicious

    Change "FadeOut" code to "FadeIn" code

    OK, changing it into a procedure instead of a function gets rid of the error. I now have this: procedure TGameBaseScreen.FadeIn; var A: TColor32; R: TColor32; G: TColor32; B: TColor32; Value: Integer; begin Value := 0; if Value = 0 then Inc(Value); //I can see what I'm doing wrong here: "Value" needs something that it can increase towards incrementally, //and then I obviously need to put in a tick count as well A := 255; R := Round(R * Value); //<--- Incompatible types error G := Round(G * Value); //<--- Incompatible types error B := Round(B * Value); //<--- Incompatible types error end; Maybe I should start simple and just see if I can draw a plain black bitmap to the screen. Tried this, it doesn't work (also tried it with A := 255). Not sure what I'm doing wrong here, but it's probably something really basic: procedure TGameBaseScreen.FadeIn; var A: TColor32; R: TColor32; G: TColor32; B: TColor32; i: Integer; begin for i := 0 to Width * Height - 1 do begin A := 0; R := 0; G := 0; B := 0; end; end;
  25. Willicious

    Change "FadeOut" code to "FadeIn" code

    OK, thanks 🙂 I'm getting an "Unsatisfied forward or external declaration" when I try to add either function to the program. Not sure what to do about this at present, but I do at least understand what the functions are meant to be doing. @Anders Melander Thanks for your help btw, apologies for my noobishness
×