Jump to content

Willicious

Members
  • Content Count

    150
  • Joined

  • Last visited

  • Days Won

    1

Everything posted by Willicious

  1. EDIT: Apologies for the rant-like nature of this post, but... I'm getting frustrated with Delphi programming. Rather than toss my laptop out the window, I'd rather express my frustration here on the Forums and hopefully it comes to something a bit more productive than a broken laptop and a feeling of regret. ----- Why can I sometimes access properties, functions, variables etc from other units, and sometimes not? "Private" and "public" doesn't seem to make any difference, either. Sometimes I'll start to type the name of a class, followed by "." and a list of all the available items comes up. I assure you, sometimes even when I make something "public", it still won't appear in that list. Why??? Also, the practice of creating "instances" of classes within procedures is ridiclously cumbersome and unintuitive. If I've created a property/variable/field/whatever in Class1.pas, it should be accessible from Class2.pas without me having to create a "version" of Class1 first. If it's declared in uses, that should be that! Forgive me for the rant, but aren't we as programmers supposed to be in charge of the program, not the other way around?!
  2. Willicious

    Problems with Delphi class structure / visibility

    Right on the money! GameParams is the global instance, that provides the address from which I can then access Renderer, Theme, and LemNames! And, it works!!! It's reassuring to know that sometimes the only way to know something is to know what to search for. And, thanks to your explanation, I actually understand what's going on here 👍
  3. Willicious

    Problems with Delphi class structure / visibility

    Yes, you're right! There is one called simply fTheme, which is declared and created in LemRendering. This would make sense, since the renderer does all of the drawing/animation work, and for that it needs the sprite types and theme colours. Problem is, I'm having the same issue trying to access LemRendering from GamePreviewScreen. To be more specific, the class in LemRendering is called TRenderer, and here's where the instance of TNeoTheme is created: constructor TRenderer.Create; begin inherited Create; fTheme := TNeoTheme.Create; LemRendering is in the uses clause at the very top of GamePreviewScreen, not the implementation/uses clause. Thanks for your detailed replies btw, very helpful 🙂👍 I'm closer to understanding the principle, but there are still times like I described in the video where I do declare an instance, but it's empty - like if one of the table builders in your example hadn't read the instructions. And, in the example here, I've managed to find an existing instance of TNeoTheme which is an actual "table builder", but I now need to know how to direct them to GamePreviewScreen to do some stuff over there. Or... don't I? Is this one of those times when declaring a new instance in GamePreviewScreen would be creating a new "table builder who has read the instructions"?
  4. Willicious

    Problems with Delphi class structure / visibility

    OK, here's a video demonstrating the issue I'm having. It comes up time and again, and sometimes I can find a fix by mimicking code elsewhere in the project. But, I'd much rather understand exactly what's going on for those times when there's nothing to mimic, and I need to fix it myself. Apologies again for the tone of the OP in this topic, I have been very frustrated with it recently and I would appreciate whatever help you can offer. I do want to try and understand as much as I can, and I realise that Delphi itself isn't the problem!
  5. Willicious

    Problems with Delphi class structure / visibility

    It happened again today when I was briefly attempting to use a property of (LemTheme unit / TTheme class) in (GamePreviewScreen Unit). The problem is that LemTheme / TTheme cannot be called as a separate instance because the original instance itself loads a particular theme for use in many different parts of the game (but unfortunately not the Preview screen). If I create a new instance of TTheme in the Preview screen logic, it will be an empty instance. The actual instance where all the stuff gets loaded is in LemTheme, in the actual TTheme class itself. This was essentially the problem I was having yesterday: I was attempting to use a new instance of TReplay, but all the actual replay data was being created and stored in a different instance (the original one, in the LemReplay unit). To use one of the analogies given here... I need to use some of the furniture in house 2 whilst I'm in house 1, but to do so is impossible without copying the furniture. As I understand it, it's bad practice to write code again that's already written somewhere else (i.e. I shouldn't have to load the Replay or the Theme again in the current Unit/Class if it's already been loaded in another Unit/Class). Instead, I should be able to access that data, but... sometimes, it just doesn't let me do so, and I'm not entirely sure why. Maybe I can make a quick video sometime demonstrating the problem. Copying and pasting code can lead to confusion, sidetracking and over-focus on context rather than getting to the actual root of the problem (which, in my case, is that I'm clearly not understanding the basic principles of OOP).
  6. Willicious

    Problems with Delphi class structure / visibility

    For what it's worth, I've managed to find a solution to the particular problem that's been bothering me today. The answer was in the existing code, I just needed to find the correct function, and the correct way to call it in another unit. However, any advice on calling properties/functions/procedures/fields/etc from another class/unit would be invaluable. I'm willing to learn! Thanks for your patience, everyone. Apologies again for the outpouring of frustration.
  7. Willicious

    Problems with Delphi class structure / visibility

    LemGame is a different unit, it contains a (variable? property? field?) called CurrentIteration which, essentially, represents the current in-game frame.
  8. 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!
  9. 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!
  10. 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.
  11. 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.
  12. 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.
  13. 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.
  14. 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.
  15. 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...?
  16. 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;
  17. 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?
  18. 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!
  19. 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.
  20. 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?
  21. Willicious

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

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

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

    Edited
  23. Willicious

    Change "FadeOut" code to "FadeIn" code

    Forgot to check Notify again.
  24. 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🤷‍♂️
  25. 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.
×