It is currently 25 Nov 2020, 00:10
   
Text Size

Card Development - talk about cards code here

Discuss Upcoming Releases, Coding New Cards, Etc.
PLEASE DO NOT REPORT BUGS HERE!

Moderators: BAgate, drool66, stassy, Aswan jaguar, gmzombie, CCGHQ Admins

DONE - Elvish Healer target opponent issue

Postby Aswan jaguar » 12 Nov 2020, 15:35

I have made Elvish Healer but not using the usual Manalink method to target the damage_card but went for the prevention shield way. The issue happens only if you target opponent. The prevention shield effect doesn't go to opponent's field like it does with other cards like Hold at Bay (shows as not owned by that player) doesn't have the point of damage on it, doesn't prevent damage and produces a rukh token at eot.

See this post for reason for and against prevention shield:
viewtopic.php?f=86&t=16985&p=194613&hilit=prevention+shield#p194626
I don't know if it is possible but if we could make prevention shield to work both ways like the regeneration shield does it would be the best solution. Although I fear that Korath didn't implement the function to use both ways because it is not possible. For me if it comes to choose one way although I like the manalink way, it produces more bugs than the shield one does, so I go for shield.
Code: Select all
int card_elvish_healer(int player, int card, event_t event){
   /* CARD_ID_ELVISH_HEALER   1912
   Elvish Healer   |2|W
   Creature - Elf Cleric 1/2
   |T: Prevent the next 1 damage that would be dealt to any target this turn. If it's a |Sgreen creature, prevent the next 2 damage instead.*/

   if( ! IS_GAA_EVENT(event) ){
      return 0;
   }

   target_definition_t td;
   default_target_definition(player, card, &td, TYPE_CREATURE|TARGET_TYPE_PLANESWALKER);
   td.preferred_controller = player;
   td.zone = TARGET_ZONE_CREATURE_OR_PLAYER;

   if (event == EVENT_CAN_ACTIVATE){
     int gaa_pre = generic_activated_ability(player, card, event, GAA_CAN_TARGET | GAA_UNTAPPED, MANACOST0, 0, &td, NULL);
     return ! gaa_pre ? 0 : (land_can_be_played & LCBP_DAMAGE_PREVENTION) ? 99 : 1; // so that it can work as a prevention shield and at manalink time.
   }

    if( event == EVENT_ACTIVATE ){
       card_instance_t* instance = get_card_instance(player, card);
       instance->number_of_targets = 0;
      generic_activated_ability(player, card, event, GAA_CAN_TARGET | GAA_LITERAL_PROMPT | GAA_UNTAPPED, MANACOST0, 0,
                          &td, "Select any target to prevent next 1 damage or 2 for a green creature.");
   }

   if( event == EVENT_RESOLVE_ACTIVATION ){
      if( valid_target(&td) ){
          card_instance_t* instance = get_card_instance(player, card);
         int targ_p = instance->targets[0].player;
         int targ_c = instance->targets[0].card;
         if( instance->targets[0].card >= 0 && (get_color(targ_p, targ_c) & get_sleighted_color_test(targ_p, targ_c, COLOR_TEST_GREEN)) ){
            prevent_the_next_n_damage(player, card, targ_p, targ_c, 2, 0, 0, 0);
         }
         else{
               prevent_the_next_n_damage(player, card, targ_p, targ_c, 1, 0, 0, 0);
         }
      }
   }

   return 0;
}
Last edited by Aswan jaguar on 21 Nov 2020, 16:34, edited 1 time in total.
Reason: tag
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7285
Joined: 13 May 2010, 12:17
Has thanked: 612 times
Been thanked: 331 times

Re: Card Development - talk about cards code here

Postby drool66 » 12 Nov 2020, 23:27

You just need to get the parent for effect resolution:
Code: Select all
   if( event == EVENT_RESOLVE_ACTIVATION ){
      if( validate_target(player, card, &td, 0) ){
          card_instance_t* instance = get_card_instance(player, card);
        card_instance_t* parent = get_card_instance(instance->parent_controller, instance->parent_card);
         int targ_p = parent->targets[0].player;
         int targ_c = parent->targets[0].card;
         if( targ_c >= 0 && (get_color(targ_p, targ_c) & get_sleighted_color_test(instance->parent_controller, instance->parent_card, COLOR_TEST_GREEN)) ){
            prevent_the_next_n_damage(instance->parent_controller, instance->parent_card, targ_p, targ_c, 2, 0, 0, 0);
         }
         else{
               prevent_the_next_n_damage(instance->parent_controller, instance->parent_card, targ_p, targ_c, 1, 0, 0, 0);
         }
      }
   }
And then it's get_sleighted_color_test(instance->parent_controller, instance->parent_card, COLOR_TEST_GREEN), not targ_p, targ_c

For finishing touches you could also use get_sleighted_color_text() in EVENT_ACTIVATE
Code: Select all
get_sleighted_color_text(player, card, "Select any target to prevent next 1 damage or 2 for a %s creature.", COLOR_GREEN)
and I think your EVENT_CAN_ACTIVATE could be simplified by just adding GAA_DAMAGE_PREVENTION

One thing I've been meaning to bring up is valid_target() vs. validate_target() in EVENT_RESOLVE_ACTIVATION/_SPELL. I think valid_target() is just "are there any valid targets?", whereas validate_target() is "is the thing I've targeted a valid target?" So if the target becomes invalid between activation/cast & resolution but there is another valid target available, valid_target() will return true when you want it to return false, whereas validate_target() will not. Is that right? I've been changing these in the cards I've worked on, but not on every one I see since it's just too many.
User avatar
drool66
 
Posts: 492
Joined: 25 Nov 2010, 22:38
Has thanked: 140 times
Been thanked: 115 times

Re: Card Development - talk about cards code here

Postby Korath » 13 Nov 2020, 04:41

drool66 wrote:I think valid_target() is just "are there any valid targets?", whereas validate_target() is "is the thing I've targeted a valid target?"
Read the source, Luke:
Code: Select all
int valid_target(target_definition_t *td ){
    return validate_target(td->player, td->card, td, 0);
}
"Are there any valid targets?" is target_available(int player, int card, target_definition_t *td).
User avatar
Korath
DEVELOPER
 
Posts: 3492
Joined: 02 Jun 2013, 05:57
Has thanked: 489 times
Been thanked: 984 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 21 Nov 2020, 17:29

With Hipparion the issue I have is that it prompts for mana once before choosing blockers ( EVENT_BLOCK_LEGALITY right? )and then re-prompts after I choose which attacker it should block. If I cancel the first prompt and pay the second it still works.
Code: Select all
int card_hipparion(int player, int card, event_t event){//in progress
   /*Hipparion   |1|W   0x000000
    * Creature - Horse 1/3
    * ~ can't block creatures with power 3 or greater unless you pay |1. */

    if (event == EVENT_BLOCK_LEGALITY && current_phase == PHASE_DECLARE_BLOCKERS && affect_me(player, card) && !is_humiliated(player, card)
       && get_power(attacking_card_controller, attacking_card) >= 3)
   {
            if(!has_mana(player, COLOR_COLORLESS, 1)){
              event_result = 1;
         }
           else if( charge_mana_while_resolving(player, card, 0, player, COLOR_COLORLESS, 1) ){
                  if(spell_fizzled !=1){
                      event_result = 0;
                     return 0;
                  }
          }
   }

   return 0;
}
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7285
Joined: 13 May 2010, 12:17
Has thanked: 612 times
Been thanked: 331 times

Re: Card Development - talk about cards code here

Postby drool66 » 21 Nov 2020, 19:04

Looking at engine.c > human_assign_blockers(), the next thing I would try would be to add "instance->state & STATE_BLOCKING" to your event conditions. EVENT_BLOCK_LEGALITY is first checked at 0x434E70 ("any_can_block") - you don't want Hipparion to trigger here. I don't see it checked in 0x4350E0 (or 0x434F30, which it calls), so maybe it's done during TRIGGER_BLOCKER_CHOSEN (which would probably call 0x434E70 again). If that's true, this fix should work, since STATE_BLOCKING is added between the two checks in human_assign_blockers().

EDIT: Found it. Event is dispatched in 0x434E70 and in is_legal_block_impl(), the latter of which is called from 0x434F30 ie. is_legal_block(). This is unfortunate because nothing meaningful is set between the two events. I guess you could use an internal variable like:
Code: Select all
int card_hipparion(int player, int card, event_t event){//in progress
   /*Hipparion   |1|W   0x000000
    * Creature - Horse 1/3
    * ~ can't block creatures with power 3 or greater unless you pay |1. */

    if (event == EVENT_BLOCK_LEGALITY && current_phase == PHASE_DECLARE_BLOCKERS && affect_me(player, card) && !is_humiliated(player, card) && get_power(attacking_card_controller, attacking_card) >= 3)
   {
      if( get_card_instance(player, card)->info_slot == 66){
            if(!has_mana(player, COLOR_COLORLESS, 1)){
              event_result = 1;
            }
           else if( !charge_mana_while_resolving(player, card, 0, player, COLOR_COLORLESS, 1) ){
                  if(spell_fizzled !=1){
                      event_result = 0;
                     return 0;
                  }
          }
        get_card_instance(player, card)->info_slot = 0;
      }
      else{
         get_card_instance(player, card)->info_slot = 66;
      }
        return 0;
   }

   return 0;
}
(just checked this & it works) It's not great, but the persistent storage is only relevant for a very short time, so not nearly as bad as some other uses of info_slot. Not 100% sure if anything could break the chain and keep info_slot set to 66 without resetting it.
User avatar
drool66
 
Posts: 492
Joined: 25 Nov 2010, 22:38
Has thanked: 140 times
Been thanked: 115 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 22 Nov 2020, 12:37

I have also fixed that you could cancel to pay mana and then be able to make the illegal block with Hipparion.
There is also an annoying little "bug" that you are asked to pay mana even if you don't have available mana (which is not the case with the previous bugged to pay twice version). If you have an idea how to fix that without messing everything else (which is what I did trying to fix this) I will wait for that tip otherwise the card works rules wise and I will commit it, thanks.
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7285
Joined: 13 May 2010, 12:17
Has thanked: 612 times
Been thanked: 331 times

Re: Card Development - talk about cards code here

Postby drool66 » 23 Nov 2020, 03:50

The version I posted above doesn't reliably clear info_slot - I just banged it out before bed. But even if it is managed properly, the dispatch from any_can_block() doesn't happen reliably due to something involving dword_4EF520[].
I have come up with a "clean-ish" solution that works 100% perfectly as far as I can tell. It involves adding a variable to check where you are in the declare attackers cycle. Looks like this:

engine.c
Code: Select all
int hack_second_dispatch_event_block_legality = 0;
void human_assign_blockers(int player)
{
[skip down to...]
  while (EXE_FN(int, 0x434E70, int)(1-player))   // any_can_block() <--first check of EVENT_BLOCK_LEGALITY; variable still 0
[...]
     if (!forbid_attack
        && select_target(player, -1000, &td_block_which_attacker, text_lines[1], &blocked))
      {
//Begin additions
        hack_second_dispatch_event_block_legality = 1; <--set to 1 before second check happens in try_block() - this is where we want to check the event
//End additions
        // 0x4350e0 clears blocking, checks legality, and then, if it was illegal, restores the previous values
        if (EXE_FN(int, 0x4350E0, int, int, int, int)(blocker.player, blocker.card, blocked.player, blocked.card))   // try_block()
         {
           int band = get_card_instance(blocked.player, blocked.card)->blocking;
           if (band == 255)
            band = blocked.card;

           card_instance_t* instance = get_card_instance(blocker.player, blocker.card);
           instance->blocking = band;
           instance->state |= STATE_BLOCKING;
           // Begin removals
           // if (band_before_being_set_to_blocked.card != 255)   // Suppresse the block sound when blocking a band
           // End removals
           play_sound_effect(WAV_BLOCK2);

           EXE_FN(void, 0x472260, void)();   // TENTATIVE_reassess_all_cards()

           if (event_flags & EA_SELECT_BLOCK)
            dispatch_trigger2(current_turn, TRIGGER_BLOCKER_CHOSEN, EXE_STR(0x790074)/*PROMPT_BLOCKERSELECTION[0]*/, 0, blocker.player, blocker.card);
         }
        else // if (ai_is_speculating != 1)   // Redundant, already checked above
         {
           load_text(0, "PROMPT_CHOOSEBLOCKERS");
           set_centerwindow_txt(text_lines[2]);
           EXE_STDCALL_FN(void, 0x4D5D32, int)(2000);   // Sleep(2000)
           set_centerwindow_txt("");
         }
//Begin additions
         hack_second_dispatch_event_block_legality = 0; <-- clear before end of loop, ie. before we go back to the dispatch in any_can_block()
//End additions
      }
   {
You then just declare the variable again in ice_age.c (preferably right before Hipparion), add "&& hack_second_dispatch_event_block_legality" to your conditions, include the fix you describe above, and viola, your card works 100% AFAIK.
This isn't an incredibly hackneyed fix, but it does feel weird to add a variable to a function called most turns of most games to serve exactly one card. On the other hand, Ice Age cards are weird and we did just add a new event just for Balduvian Shaman, so eh...
As a side note, TRIGGER_PAY_TO_BLOCK won't work because it's dispatched before blockers are chosen, and this tax depends on the chosen blocker.
User avatar
drool66
 
Posts: 492
Joined: 25 Nov 2010, 22:38
Has thanked: 140 times
Been thanked: 115 times

Re: Card Development - talk about cards code here

Postby Aswan jaguar » 23 Nov 2020, 15:39

Yes, it works better with this variable. I think also Awesome Presence needs the same or similar approach but I am not touching it, as it is more complicated than Hipparion and I don't want to ](*,) .
Current card's code (I don't include the needed changes in engine.c that you have already posted) & (still prompts even if you don't have available mana but at least now works fine ).
Code: Select all
int hack_second_dispatch_event_block_legality;
int card_hipparion(int player, int card, event_t event){//in progress
   /*Hipparion   |1|W
   * Creature - Horse 1/3
   * ~ can't block creatures with power 3 or greater unless you pay |1. */

   if (event == EVENT_BLOCK_LEGALITY && current_phase == PHASE_DECLARE_BLOCKERS && affect_me(player, card) && !is_humiliated(player, card)
      && get_power(attacking_card_controller, attacking_card) >= 3 && hack_second_dispatch_event_block_legality)
   {
      if(!has_mana(player, COLOR_COLORLESS, 1)){
         event_result = 1;
      }
      else if( !charge_mana_while_resolving(player, card, 0, player, COLOR_COLORLESS, 1) ){
             if(spell_fizzled !=1){
               event_result = 0;
               return 0;
             }
             event_result = 1;
      }
   }

    return 0;
}
EDIT: Unfortunately I just tested Hipparion against cards with "must block if able" and with power bigger than two and although initially finds the block illegal you get prompt again and then it doesn't find it as illegal block anymore. I tested with Avalanche Tusker (after fixing it to target creatures) but any other will do. I tested also with Blaze of Glory and there things were worse as I didn't get prompt at all and Hipparion was forced to block the three illegal blockers without paying mana.
---
Trying to squash some bugs and playtesting.
User avatar
Aswan jaguar
Super Tester Elite
 
Posts: 7285
Joined: 13 May 2010, 12:17
Has thanked: 612 times
Been thanked: 331 times

Re: Card Development - talk about cards code here

Postby drool66 » 24 Nov 2020, 18:41

Oof, that's real Gordian Knot. So SP_KEYWORD_MUST_BLOCK forwards to block_if_able() on EVENT_DECLARE_BLOCKERS, then to select_blocker(), whose target definition includes can_block_target(), calling can_block_me(), which returns is_legal_block() / is_legal_block_impl(), which returns the event_result (well, actually its binary opposite) of EVENT_BLOCK_LEGALITY. Issue is I can't tell exactly when EVENT_DECLARE_BLOCKERS is dispatched. FWIW, here is my current state of hippparion & human_assign_blockers() - most of it is a mess & will probably end up being unnecessary, but I'm trying to isolate this call to EVENT_BLOCK_LEGALITY:
Hipparion:
Code: Select all
int hack_first_call_to_event_block_legality;
int hack_second_call_to_event_block_legality;
int hack_cancel_blockers;
int card_hipparion(int player, int card, event_t event){//in progress
   /*Hipparion   |1|W   0x000000
    * Creature - Horse 1/3
    * ~ can't block creatures with power 3 or greater unless you pay |1. */

   if ((event == EVENT_BLOCK_LEGALITY || event == EVENT_DECLARE_BLOCKERS) && current_phase == PHASE_DECLARE_BLOCKERS && affect_me(player, card) && !is_humiliated(player, card) && get_power(attacking_card_controller, attacking_card) >= 3 ){
      ASSERT(!(get_card_instance(player, card)->info_slot == 0xFF));
      if( hack_cancel_blockers ){
         get_card_instance(player, card)->info_slot |= 0xF;
         get_card_instance(player, card)->targets[16].card &= ~SP_KEYWORD_MUST_BLOCK;
      }
      if( hack_second_call_to_event_block_legality && !(get_card_instance(player, card)->info_slot & 0xFF)){
         if(!has_mana(player, COLOR_COLORLESS, 1)){
            event_result |= 1;
         }
         else if( !charge_mana_while_resolving(player, card, 0, player, COLOR_COLORLESS, 1) ){
            if(spell_fizzled !=1){
               get_card_instance(player, card)->info_slot |= 0xF0;//paid
               get_card_instance(player, card)->info_slot &= ~0xF;//canceled
               event_result = 0;
               return 0;
            }
            get_card_instance(player, card)->info_slot |= 0xF;//canceled
            event_result |= 1;
         }
      }
      else if(!hack_first_call_to_event_block_legality && !(get_card_instance(player, card)->info_slot & 0xFF) ){//outside of normal blocking loop & hasn't been prompted
         if(!has_mana(player, COLOR_COLORLESS, 1)){
            event_result |= 1;
         }
         else if( !charge_mana_while_resolving(player, card, 0, player, COLOR_COLORLESS, 1) ){
            if(spell_fizzled !=1){
               get_card_instance(player, card)->info_slot |= 0xF0;//paid
               get_card_instance(player, card)->info_slot &= ~0xF;//canceled
               event_result = 0;
               return 0;
            }
            get_card_instance(player, card)->info_slot |= 0xF;//canceled
            event_result |= 1;
         }
      }
      else if( hack_first_call_to_event_block_legality && !(get_card_instance(player, card)->info_slot & 0xF0) && !has_mana(player, COLOR_COLORLESS, 1))
         event_result |= 1;
      else if(!hack_first_call_to_event_block_legality && get_card_instance(player, card)->info_slot & 0xF0 && !(get_card_instance(player, card)->info_slot & 0xF))
         event_result = 0;
      else if(!hack_first_call_to_event_block_legality && get_card_instance(player, card)->info_slot & 0xF && !(get_card_instance(player, card)->info_slot & 0xF0))
         event_result |= 1;
   }

   if( event == EVENT_PHASE_CHANGED && (current_phase >= PHASE_MAIN2 || current_phase <= PHASE_MAIN1))
      get_card_instance(player, card)->info_slot = 0;

   return 0;
}
human_assign_blockers()
Code: Select all
int hack_first_call_to_event_block_legality = 0;
int hack_second_call_to_event_block_legality = 0;
int hack_cancel_blockers = 0;
void human_assign_blockers(int player)
{
  // 0x434960

  if (ai_is_speculating == 1)
   return;

  // Begin additions
  int who_chooses;
  if (event_flags & EF_ATTACKER_CHOOSES_BLOCKERS)
   {
     who_chooses = player;
     if (current_turn == AI)   // Nothing blocks
      return;
   }
  else
   who_chooses = 1-player;
  // End additions

  EXE_FN(void, 0x472260, void)();   // TENTATIVE_reassess_all_cards()

  // Begin removals
#if 0
  // This seems to be the remains of primitive AI handling; the values it computes aren't used.
  int indices[16], powers[16], highest_power = 0, pos = 0, c;
  for (c = 0; active_cards_count[player] > c; ++c)
   {
     card_instance_t* instance = get_card_instance(player, c);
     if (instance->internal_card_id != -1 && (instance->state & STATE_ATTACKING))
      {
        int pow = get_abilities(player, c, EVENT_POWER, -1);
        indices[pos] = c;
        powers[pos] = pow;
        ++pos;
        if (highest_power < pow)
         highest_power = pow;
      }
   }
#endif
  // End removals

  target_definition_t td_choose_blocker;
  base_target_definition(1-player, 0, &td_choose_blocker, TARGET_TYPE_NONCREATURE_CAN_BLOCK | TYPE_CREATURE);
  td_choose_blocker.who_chooses = who_chooses;
  td_choose_blocker.allowed_controller = 1-player;
  td_choose_blocker.preferred_controller = 1-player;
  td_choose_blocker.zone = TARGET_ZONE_0x2000 | TARGET_ZONE_IN_PLAY;
  td_choose_blocker.special = TARGET_SPECIAL_ALLOW_MULTIBLOCKER;
  td_choose_blocker.illegal_state = TARGET_STATE_BLOCKING | TARGET_STATE_TAPPED;
  td_choose_blocker.allow_cancel = 2;   // I suspect this is what makes the "Done" button.

  target_definition_t td_block_which_attacker;
  base_target_definition(player, 0, &td_block_which_attacker, TYPE_CREATURE);
  td_block_which_attacker.who_chooses = who_chooses;
  td_block_which_attacker.allowed_controller = player;
  td_block_which_attacker.preferred_controller = player;
  td_block_which_attacker.required_state = TARGET_STATE_ATTACKING;

  target_t blocker, blocked;

  hack_first_call_to_event_block_legality = 1;
  while (EXE_FN(int, 0x434E70, int)(1-player))   // any_can_block()
   {
     hack_first_call_to_event_block_legality = 0;
     load_text(0, "PROMPT_CHOOSEBLOCKERS");
     if (!select_target(1-player, -1000, &td_choose_blocker, text_lines[0], &blocker))
      return;

     forbid_attack = 0;
     if (event_flags & EA_PAID_BLOCK)
      {
        push_affected_card_stack();

        trigger_cause_controller = blocker.player;
        trigger_cause = blocker.card;
        dispatch_trigger(1-player, TRIGGER_PAY_TO_BLOCK, EXE_STR(0x790248)/*PROMPT_TURNSEQUENCE[0]*/, 1);
        pop_affected_card_stack();
      }

     if (!forbid_attack
        && select_target(player, -1000, &td_block_which_attacker, text_lines[1], &blocked))
      {
        hack_second_call_to_event_block_legality = 1;
        // 0x4350e0 clears blocking, checks legality, and then, if it was illegal, restores the previous values
        if (EXE_FN(int, 0x4350E0, int, int, int, int)(blocker.player, blocker.card, blocked.player, blocked.card))   // try_block()
         {
           hack_second_call_to_event_block_legality = 0;
           int band = get_card_instance(blocked.player, blocked.card)->blocking;
           if (band == 255)
            band = blocked.card;

           card_instance_t* instance = get_card_instance(blocker.player, blocker.card);
           instance->blocking = band;
           instance->state |= STATE_BLOCKING;
           // Begin removals
           // if (band_before_being_set_to_blocked.card != 255)   // Suppresse the block sound when blocking a band
           // End removals
           play_sound_effect(WAV_BLOCK2);

           EXE_FN(void, 0x472260, void)();   // TENTATIVE_reassess_all_cards()

           if (event_flags & EA_SELECT_BLOCK)
            dispatch_trigger2(current_turn, TRIGGER_BLOCKER_CHOSEN, EXE_STR(0x790074)/*PROMPT_BLOCKERSELECTION[0]*/, 0, blocker.player, blocker.card);
         }
        else // if (ai_is_speculating != 1)   // Redundant, already checked above
         {
           hack_cancel_blockers = 1;
           load_text(0, "PROMPT_CHOOSEBLOCKERS");
           set_centerwindow_txt(text_lines[2]);
           EXE_STDCALL_FN(void, 0x4D5D32, int)(2000);   // Sleep(2000)
           set_centerwindow_txt("");
         }
      }
     hack_first_call_to_event_block_legality = 1;
   }
  hack_first_call_to_event_block_legality = 0;
  hack_cancel_blockers = 0;
}
User avatar
drool66
 
Posts: 492
Joined: 25 Nov 2010, 22:38
Has thanked: 140 times
Been thanked: 115 times

Previous

Return to Development

Who is online

Users browsing this forum: No registered users and 2 guests


Who is online

In total there are 2 users online :: 0 registered, 0 hidden and 2 guests (based on users active over the past 10 minutes)
Most users ever online was 1371 on 09 Feb 2020, 16:22

Users browsing this forum: No registered users and 2 guests

Login Form