设置具有返回值的eventHandler

时间:2014-01-25 19:02:00

标签: delphi delphi-xe2

我需要调用事件处理程序来告诉用户选择TCardTLabel 然后将此值作为参数返回。

我有两个单位GAMESS_SPELL

这就是SS_SPEll中的代码如何工作。

// TSPELL
// ======
// The chronology is:
// 1.  At appropriate points in the game (such as before turn, after cast etc aka trigger points)
//     the game calls the SpellMeister's RunSpells method.
// 2.  RunSpells checks the database for spells matching the card that
//     initiated the spell action, and the trigger point at which it did so.
//     For each one that it finds it will create an appropriate object, which
//     could be a TSpell descendent or a TSpellAdjuster descendent.
//     For each TSpell it finds it fires off an onFindSpell event.
//     See below for details of how TSpellAdjusters are handled.
// 3.  The handler for the onFindSpell events can (should) call the spells'
//     AimAt method for each potential target. A potential target is a card
//     or a player.
// 4.  A spell's AimAt method checks if the potential target is a legitimate
//     target for that spell and if so it calls its ApplySpellTo method to
//     actually do the dirty deed.

所以我需要的是一旦RunSpells获得db信息,它将检查needs2ndtarget:= 1,如果是,那么我知道我需要这个法术的第二个目标。

以下是TSpellBase,它是从TSpell创建的。为了创建此活动,我添加了FOnSeek2ndTarget 在私有部分和受保护的FNeed2Target和公共财产OnSeek2ndTarget。你也会看到 TTargetEvt,目前设置为TCARD,但我需要它来回复TCard或TLAbel,不知道如何做到这一点。

 TTargetEvt = procedure (Card : TCard) of Object;  


 TSpellBase = class
  private
    FOnManaChange: TManaEvt;
    FOnSeek2ndTarget: TTargetEvt;

  protected
    FCardType            : TCardType;
    FOriginator          : TCard;
    FNeed2Target         : integer;

    function  LegitimateTarget    (Candidate : TObject) : boolean; virtual;
  public
    constructor Create; virtual;

    property CardType    : TCardType     read FCardType     write FCardType;    // ctLava, ctNature, ctWizard, etc etc
    property Originator  : TCard         read FOriginator   write FOriginator;

    property Need2Target : integer       read FNeed2Target  write FNeed2Target;
    property OnManaChange  : TManaEvt  read FOnManaChange  write FOnManaChange;
    property OnSeek2ndTarget : TTargetEvt read FonSeek2ndTarget write FOnSeek2ndTarget;
  end;

现在在TSpell,我不认为我需要这里的任何东西,但它需要spellmiester.runspell程序

TSpell = class(TSpellBase)
  private
  protected
    FCategory    : TCategory;
    FLifeToAdd   : Byte;
    FMaxRandom   : Byte;
    FReplaceDmg  : Byte;
    FReplacement : string;
    FStatTarget  : Byte;
    FTrigger     : TTrigger;
    procedure ApplySpellTo(Target : TObject); virtual; abstract; // Apply the spell to the target
  public
    procedure AimAt(Candidate: TObject); virtual;

    property Category    : TCategory     read FCategory     write FCategory;
    property LifeToAdd   : Byte          read FLifeToAdd    write FLifeToAdd;
    property MaxRandom   : Byte          read FMaxRandom    write FMaxRandom;
    property ReplaceDmg  : Byte          read FReplaceDmg   write FReplaceDmg;
    property Replacement : string        read FReplacement  write FReplacement;
    property StatTarget  : Byte          read FStatTarget   write FStatTarget;
    property Trigger     : TTrigger      read FTrigger      write FTrigger;
  end;

这里我在私人部分添加了FOnSeek2ndTarget。和属性所以现在当一个咒语被施放时它会   到这里,现在打电话给runsspells。

 TSpellMeister = class
  private
    FonFindSpell   : TRcvSpell;
    FOnManaChange  : TManaEvt;
    FOnSeek2ndTarget  : TTargetEvt;
//    FonNewAdjuster : TRcvSpell;
  protected
    FAdjusters : TAdjusters;
    FQuery : TADOQuery;

  public
    constructor Create(DBCon: TADOConnection);
    destructor Destroy; override;

    function IfNull( const Value, Default : OleVariant ) : OleVariant;
    procedure Adjust(Attacker : TCard; Victim : TObject; var TheDamage : integer); overload;
    procedure Adjust(Attacker : TCard;                   var TheCost   : integer); overload;
    procedure RunSpells(Card : TCard; Trigger : TTrigger);

    property onFindSpell   : TRcvSpell read FonFindSpell   write FonFindSpell;
    property OnManaChange  : TManaEvt  read FOnManaChange  write FOnManaChange;
    property OnSeek2ndTarget: TTargetEvt read FOnSeek2ndTarget write FOnSeek2ndTarget;
//    property onNewAdjuster : TRcvSpell read FonNewAdjuster write FonNewAdjuster;
  end;

这就是问题所在,我添加foundspell.Need2ndTarget这会从数据库中获取数据(如果是1)则需要用户选择该法术的另一个目标。目前我添加了

如果FoundSpell.Need2Target = 1那么              FOnSeek2ndTarget(卡);

但我确信这不正确......

 //**************************************************************************
procedure TSpellMeister.RunSpells(Card: TCard; Trigger: TTrigger);
//**************************************************************************
var
  OneSpell : TSpellBase;
  FoundSpell : TSpell;  // Just so only have to cast once
begin
  assert(assigned(FonFindSpell),'TSpellMeister.RunSpells : No onFindSpell event handler!');
  // Search the database
  FQuery.Active := FALSE;
  FQuery.Parameters.ParamByName(SQL_PARAM_SPELL_ORIGINATOR).Value := Card.CName;
  FQuery.Parameters.ParamByName(SQL_PARAM_SPELL_TRIGGER   ).Value := Trigger;
  FQuery.Active := TRUE;

  // Iterate through the spell records. For each one, create a category-specific
  // TSpell descendant and fire off an onFindSpell event.

  if FQuery.RecordCount > 0 then
  begin
    FQuery.Recordset.MoveFirst;
    while not FQuery.Recordset.EOF do
    begin
      case TCategory(FQuery.Recordset.Fields[DB_FLD_CATEGORY].Value) of
        caAboveLife               : OneSpell := TSpellAboveLife.Create;
        caDamage                  : OneSpell := TSpellDamage.Create;
        caDamagePlus              : OneSpell := TSpellDamagePlus.Create;
        caDamagePlusPercent       : OneSpell := TSpellDamagePlusPercent.Create;
        caDamagePercentIncrease   : OneSpell := TSpellDamagePercentIncrease.Create;
        caDamagePercentDecrease   : OneSpell := TSpellDamagePercentDecrease.Create;
        caDamageSpells            : OneSpell := TSpellDamageSpells.Create;
        caDestroy                 : OneSpell := TSpellDestroy.Create;
        .....
        else                        raise ERangeError.CreateFmt(ERROR_INVALID_DB_NUMBER,[DB_FLD_CATEGORY,FQuery.Recordset.Fields[DB_FLD_CATEGORY].Value]);
      end;
      try
        if assigned(OneSpell) then
        begin
          OneSpell.CardType     := TCardType  (IfNull( FQuery.Recordset.Fields[ DB_FLD_CARD_TYPE   ].Value,0) );
          OneSpell.Originator   := Card;
          OneSpell.OnManaChange := Self.OnManaChange;
          OneSpell.OnSeek2ndTarget := self.OnSeek2ndTarget;
          assert(OneSpell.Originator.COwner is TPlayer,'TSpellMeister.RunSpells : OneSpell.Originator.COwner not a player: ' + OneSpell.Originator.COwner.ClassName);
            try
              FoundSpell := TSpell(OneSpell);
              FoundSpell.Originator  := Card;
              FoundSpell.Trigger     := Trigger;
              FoundSpell.CardType    := TCardType  ( FQuery.Recordset.Fields[ DB_FLD_CARD_TYPE   ].Value );
              FoundSpell.Category    := TCategory  ( FQuery.Recordset.Fields[ DB_FLD_CATEGORY    ].Value );
              FoundSpell.LifeToAdd   :=             IfNull( FQuery.Recordset.Fields[ DB_FLD_LIFE_TO_ADD ].Value,0);
              FoundSpell.MaxRandom   :=             IfNull( FQuery.Recordset.Fields[ DB_FLD_MAX_RANDOM  ].Value,0);
              FoundSpell.PerCent     :=             IfNull( FQuery.Recordset.Fields[ DB_FLD_PER_CENT    ].Value,0);
              FoundSpell.Plus        :=             IfNull( FQuery.Recordset.Fields[ DB_FLD_PLUS        ].Value,0);
              FoundSpell.ReplaceDmg  :=             IfNull( FQuery.Recordset.Fields[ DB_FLD_REPLACE_DMG ].Value,0);
              FoundSpell.Replacement :=             IfNull( FQuery.Recordset.Fields[ DB_FLD_REPLACEMENT ].Value,0);
              FoundSpell.StatTarget  :=             IfNull( FQuery.Recordset.Fields[ DB_FLD_STAT_TARGET ].Value,0);
              FoundSpell.Target      := TTargetType( IfNull(FQuery.Recordset.Fields[ DB_FLD_TARGET      ].Value,0) );
              FoundSpell.Need2Target :=             IfNull( FQuery.Recordset.Fields[ DB_FLD_NEED2TARGET ].Value,0);
              assert(FoundSpell.Originator.COwner is TPlayer,'TSpellMeister.RunSpells : FoundSpell.Originator.COwner not a player: ' + OneSpell.Originator.COwner.ClassName);
              if FoundSpell.Need2Target = 1 then
                 FOnSeek2ndTarget(Card);
              FonFindSpell(FoundSpell);
            finally
              FreeAndNil(OneSpell);
            end;
            end;

      except                    // I think this is OK but is there a possible bug if
        FreeAndNil(OneSpell);   //  spell adjuster added to list then destroyed?
      end;                      // List item would then be invalid.

      FQuery.Recordset.MoveNext;
    end;
  end;
end;

所以这就是ss_spells单元,现在是使用ss_spells单元的game单元 在forum.create我有

 FSpellMeister.OnSeek2ndTarget := self.Handle2ndTarget; 

不知道在Handle2ndTarget中放入什么只是

//****************************************************************************
procedure TFGame.Handle2ndTarget(Card : TCard);
begin
  showmessage('Select a target HANDLE2ndTARGET');
end;

看看我是否至少能够到达这里..

因此,如果您无法解决这个问题,那么当foundspell.Need2ndTarget := 1使用事件FOnSeek2ndTarget();

时,如何将ss_Spells中的var设置为TObject(tcard或tlabel)

2 个答案:

答案 0 :(得分:4)

只需更改TTargetEvt类型的签名,例如:

TTargetEvt = procedure (Card : TCard; var Target: TObject) of Object;  

然后相应地更新RunSpells()

var
  Target: TObject;
...
if FoundSpell.Need2Target = 1 then
begin
  Target := nil;
  if Assigned(FOnSeek2ndTarget) then FOnSeek2ndTarget(Card, Target);
  // use Target as needed...
end;
...

然后相应地更新您的处理程序:

procedure TFGame.Handle2ndTarget(Card : TCard; var Target: TObject);
begin
  Target := ...;
end;

答案 1 :(得分:2)

  

我需要知道如何使用事件来返回var参数。

像这样定义您的活动:

type
  TMyEvent = procedure(var ReturnValue: Integer) of object;

然后以通常的方式添加事件属性:

....
private
  FOnMyEvent: TMyEvent;
....
published
  property OnMyEvent: TMyEvent read FOnMyEvent write FOnMyEvent;
....

细微差别在于你如何展示事件。通常,如果您正在编写提供事件的组件,则必须满足不会在事件中附加处理程序的可能性。如果事件是为了返回一个值,那么你怎么能没有犯规者,也没有返回值?诀窍是在展示事件之前将参数指定为默认值。例如:

procedure TMyComponent.DoMyEvent(out ReturnValue: Integer);
begin
  Result := DefaultValueForMyEventHandler;// you supply something meaningful here
  if Assigned(FOnMyEvent) then
    FOnMyEvent(Result);
end;

因此,如果组件的使用者没有为事件提供处理程序,那么该方法仍然会产生合理的值。

如果您阅读此内容并认为FOnMyEvent nil没有任何意义,那么您的设计是错误的。如果要强制使用者提供行为,而不允许依赖于默认值,则事件是错误的机制。在这种情况下,要求消费者通过参数提供行为,可能由组件构造函数的签名强制执行。或者其他一些方式。

我刚给你一个与你的代码无关的基本示例。我试图了解这些概念。希望您能够根据您的特定需求进行调整。