void CombatManager::QueueNextEvent(psCombatGameEvent *event)
{
    QueueNextEvent(event->GetAttacker(),
                   event->GetWeaponSlot(),
                   event->GetTarget(),
                   event->GetAttackerID(),
                   event->GetTargetID(),
                   event->GetAttackResult());
}
示例#2
0
void WeatherManager::StartWeather(psSectorInfo* si, unsigned int type)
{
    // Is rain/snow enabled for this sector
    if((type == 0 || type == psWeatherMessage::RAIN) && si->GetWeatherEnabled((unsigned int)psWeatherMessage::RAIN))
    {
        // Queue event to start rain
        QueueNextEvent(si->GetRandomWeatherGap((unsigned int)psWeatherMessage::RAIN),
                       psWeatherMessage::RAIN,
                       si->GetRandomWeatherDensity((unsigned int)psWeatherMessage::RAIN),
                       0, // Duration is calculated when stop event is created
                       0, // Fade is calculated when sending weather event
                       si->name,
                       si);
    }
    if((type == 0 || type == psWeatherMessage::SNOW) && si->GetWeatherEnabled((unsigned int)psWeatherMessage::SNOW))
    {
        // Queue event to start snow
        QueueNextEvent(si->GetRandomWeatherGap((unsigned int)psWeatherMessage::SNOW),
                       psWeatherMessage::SNOW,
                       si->GetRandomWeatherDensity((unsigned int)psWeatherMessage::SNOW),
                       0, // Duration is calculated when stop event is created
                       0, // Fade is calculated when sending weather event
                       si->name,
                       si);
    }
    if((type == 0 || type == psWeatherMessage::FOG) && si->GetWeatherEnabled((unsigned int)psWeatherMessage::FOG))
    {
        // Queue event to start snow
        QueueNextEvent(si->GetRandomWeatherGap((unsigned int)psWeatherMessage::FOG),
                       psWeatherMessage::FOG,
                       si->GetRandomWeatherDensity((unsigned int)psWeatherMessage::FOG),
                       0, // Duration is calculated when stop event is created
                       si->GetRandomWeatherFadeIn((unsigned int)psWeatherMessage::FOG),
                       si->name,
                       si, 0, 200, 200, 200);
    }
}
示例#3
0
void WeatherManager::StartGameTime()
{
    csString lastTime,lastDate;

    // Load time
    psserver->GetServerOption("game_time", lastTime);
    sscanf(lastTime.GetDataSafe(),"%d:%d",&gameTimeHour,&gameTimeMinute);

    // Load date
    psserver->GetServerOption("game_date", lastDate);
    sscanf(lastDate.GetDataSafe(),"%d-%d-%d",&gameTimeYear,&gameTimeMonth,&gameTimeDay);

    // Start the time of day clock
    QueueNextEvent(0, psWeatherMessage::DAYNIGHT, 0, 0, 0, "", NULL);
}
示例#4
0
void WeatherManager::HandleWeatherEvent(psWeatherGameEvent* event)
{
    {
        CS::Threading::MutexScopedLock lock(eventsMutex);
        events.Delete(event); // Delete this from our "db"
    }

    // See if we want to ignore this event
    for(size_t i = 0; i < ignored.GetSize(); i++)
    {
        if(event == ignored[i])
        {
            ignored.DeleteIndex(i);
            return;
        }
    }

    switch(event->type)
    {
    case psWeatherMessage::SNOW:
    case psWeatherMessage::RAIN:
    {
        event->si->current_rain_drops = event->value;

        Notify4(LOG_WEATHER,"New %s in sector '%s': %d",event->type == psWeatherMessage::SNOW ? "snow" : "rain",  event->si->name.GetData(),event->value);

        psWeatherMessage::NetWeatherInfo info;

        info.has_downfall     = true;
        info.downfall_is_snow = (event->type == psWeatherMessage::SNOW);
        info.has_fog          = true;
        info.has_lightning    = false;

        info.downfall_drops = event->value;

        // Save current fog and calculate new.
        if(event->value)
        {
            // Only save the fog 'history' once. After that we just override.
            if(!event->si->densitySaved)
            {
                event->si->fog_density_old = event->si->fog_density;
                event->si->densitySaved = true;
            }

            // Set fog colour if there's not already fog.
            if(!event->si->fog_density)
            {
                event->si->r = 255;
                event->si->g = 255;
                event->si->b = 255;
            }

            if(info.downfall_drops < 8000)
            {
                // Calculate fog to be linear in range 0 to 200
                event->si->fog_density =
                    (int)(200.0f*(info.downfall_drops-1000.0f)/8000.0f);
            }
            else
            {
                event->si->fog_density = 200;
            }
        }
        else if(event->si->fog_density)
        {
            // Restore fog, if the fog wasn't turned off.
            event->si->fog_density = event->si->fog_density_old;
            event->si->densitySaved = false;
        }

        info.fog_density = event->si->fog_density;

        info.r = event->si->r;
        info.g = event->si->g;
        info.b = event->si->b;

        info.sector = event->sector;

        if(event->fade)
        {
            info.downfall_fade = event->fade;
            info.fog_fade = event->fade;
        }
        else
        {
            if(event->value)
            {
                info.downfall_fade = event->si->GetRandomWeatherFadeIn((unsigned int)event->type);
                info.fog_fade = info.downfall_fade;
            }
            else
            {
                info.downfall_fade = event->si->GetRandomWeatherFadeOut((unsigned int)event->type);
                info.fog_fade = info.downfall_fade;
            }
        }

        Notify4(LOG_WEATHER,"Drops: %d Density: %d Sector: %s\n",
                info.downfall_drops,info.fog_density,info.sector.GetDataSafe());

        psWeatherMessage rain(0,info);
        if(rain.valid)
            psserver->GetEventManager()->Broadcast(rain.msg,NetBase::BC_EVERYONE);
        else
        {
            Bug1("Could not create valid psWeatherMessage (rain) for broadcast.\n");
        }

        // Make sure we don't have any other events in this sector that will disturb
        // Simple case is when event types are equal. In addition we have to test
        // for the mutal exclusive case where we are changing from snow to rain or
        // rain to snow.

        {
            CS::Threading::MutexScopedLock lock(eventsMutex);
            for(size_t i = 0; i < events.GetSize(); i++)
            {
                psWeatherGameEvent* evt = events[i];
                if(evt->sector == event->sector && (evt->type == event->type
                                                    || ((evt->type == psWeatherMessage::RAIN || evt->type
                                                            == psWeatherMessage::SNOW) && (event->type
                                                                    == psWeatherMessage::RAIN || event->type
                                                                    == psWeatherMessage::SNOW))))
                {
                    ignored.Push(evt); // Ignore when the eventmanager handles the event
                    events.DeleteIndex(i);
                    i--;

                    Notify4(LOG_WEATHER,"Removed disturbing event for sector '%s' (%d,%d)",
                            evt->sector.GetData(),evt->value,evt->duration);

                }
            }
        }

        if(event->value)   // Queue event to turn off rain/snow.
        {
            if(event->type != psWeatherMessage::SNOW)
                event->si->is_raining = true;
            else
                event->si->is_snowing = true;

            if(event->si->GetWeatherEnabled((unsigned int) psWeatherMessage::LIGHTNING) &&
                    event->value > 2000 && event->si->is_raining)
            {
                // Queue lightning during rain storm here first
                QueueNextEvent(event->si->GetRandomWeatherGap((unsigned int) psWeatherMessage::LIGHTNING),
                               psWeatherMessage::LIGHTNING,
                               0,
                               0,
                               0,
                               event->si->name,
                               event->si,
                               event->clientnum);
            }

            // Queue event to stop rain/snow
            int duration;
            if(event->duration != -1)
            {
                if(event->duration)
                {
                    duration = event->duration;
                }
                else
                {
                    duration = event->si->GetRandomWeatherDuration((unsigned int) event->type);
                }

                QueueNextEvent(duration,
                               event->type,
                               0,
                               0,
                               event->fade,
                               event->si->name,
                               event->si);
            }

        }
        else // Stop rain/snow.
        {
            if(event->type== psWeatherMessage::SNOW)
                event->si->is_snowing = false;
            else
                event->si->is_raining = false;

            // Queue event to turn on again later if enabled
            StartWeather(event->si);
        }

        break;
    }
    case psWeatherMessage::FOG:
    {
        // Update sector weather info
        event->si->fog_density = event->value;
        event->si->r = event->cr;
        event->si->g = event->cg;
        event->si->b = event->cb;

        // Update the clients
        psWeatherMessage::NetWeatherInfo info;
        info.has_downfall     = false;
        info.downfall_is_snow = false;
        info.has_fog          = true;
        info.has_lightning    = false;
        info.sector           = event->si->name;
        info.fog_density      = event->si->fog_density;
        info.r                = event->cr;
        info.g                = event->cg;
        info.b                = event->cb;
        info.downfall_drops   = 0;
        info.downfall_fade    = 0;

        Notify3(LOG_WEATHER,"New Fog in sector '%s': %d",  event->si->name.GetData(), event->value);

        // Save the fade in so we can reverse it when fading out.
        if(event->fade)
        {
            event->si->fogFade = event->fade;
        }
        else
        {
            // We're removing fog, so removed the 'saved' flag.
            event->si->densitySaved = false;
        }
        info.fog_fade = event->si->fogFade;

        psWeatherMessage fog(0,info);
        if(fog.valid)
            psserver->GetEventManager()->Broadcast(fog.msg,NetBase::BC_EVERYONE);
        else
        {
            Bug1("Could not create valid psWeatherMessage (fog) for broadcast.\n");
        }

        if(event->value)   // Queue event to turn off fog.
        {

            // Queue event to stop rain/snow
            int duration;
            if(event->duration != -1)
            {
                if(event->duration)
                {
                    duration = event->duration;
                }
                else
                {
                    duration = event->si->GetRandomWeatherDuration((unsigned int) event->type);
                }

                QueueNextEvent(duration,
                               event->type,
                               0,
                               0,
                               event->si->GetRandomWeatherFadeOut((unsigned int)psWeatherMessage::FOG),
                               event->si->name,
                               event->si);
            }

        }
        else // Stop fog.
        {
            // Queue event to turn on again later if enabled
            StartWeather(event->si, psWeatherMessage::FOG);
        }

        break;
    }
    case psWeatherMessage::LIGHTNING:
    {
        if(event->si->is_raining)
        {
            Notify2(LOG_WEATHER,"Lightning in sector '%s'",event->sector.GetData());

            psWeatherMessage::NetWeatherInfo info;
            info.has_downfall  = false;
            info.downfall_is_snow = false;
            info.has_fog       = false;
            info.has_lightning = true;
            info.sector = event->sector;
            info.fog_fade = info.downfall_fade = 0;
            info.r = info.g = info.b = 0;
            info.downfall_drops = info.fog_density = 0;

            psWeatherMessage lightning(0,info, event->clientnum);

            if(lightning.valid)
            {
                psserver->GetEventManager()->Broadcast(lightning.msg);
            }
            else
            {
                Bug1("Could not create valid psWeatherMessage (lightning) for broadcast.\n");
            }

            if(event->si->is_raining &&
                    event->si->GetWeatherEnabled((unsigned int) psWeatherMessage::LIGHTNING))
            {
                QueueNextEvent(event->si->GetRandomWeatherGap((unsigned int) psWeatherMessage::LIGHTNING),
                               psWeatherMessage::LIGHTNING,
                               0,
                               0,
                               0,
                               event->si->name,
                               event->si,
                               event->clientnum);
            }
        }
        break;
    }
    case psWeatherMessage::DAYNIGHT:
    {
        QueueNextEvent(GAME_MINUTE_IN_TICKS,
                       psWeatherMessage::DAYNIGHT,
                       0,
                       0,
                       0,
                       NULL,
                       NULL);

        gameTimeMinute++;
        if(gameTimeMinute >= 60)
        {
            gameTimeMinute = 0;
            gameTimeHour++;
            if(gameTimeHour >= 24)
            {
                gameTimeHour = 0;
                gameTimeDay++;
                if(gameTimeDay >= monthLengths[gameTimeMonth-1]+1)
                {
                    gameTimeDay = 1;
                    gameTimeMonth++;
                    if(gameTimeMonth >= MONTH_COUNT+1)
                    {
                        gameTimeMonth = 1;
                        gameTimeYear++;
                    }
                }

            }
            // Only save and broadcast every game hour.
            SaveGameTime();
            BroadcastGameTime();
        }
        else
        {
            // Super clients should get the time every minute
            BroadcastGameTimeSuperclients();
        }

        break;
    }
    default:
    {
        break;
    }
    }
}
void CombatManager::HandleCombatEvent(psCombatGameEvent *event)
{
    psCharacter *attacker_data,*target_data;
    int attack_result;
    bool skipThisRound = false;

    if (!event->GetAttacker() || !event->GetTarget()) // disconnected and deleted
    {
#ifdef COMBAT_DEBUG
        psserver->SendSystemError(event->AttackerCID, "Combat stopped as one participant logged of.");
#endif
        return;
    }

    gemActor *gemAttacker = dynamic_cast<gemActor*> ((gemObject *) event->attacker);
    gemActor *gemTarget   = dynamic_cast<gemActor*> ((gemObject *) event->target);

    attacker_data=event->GetAttackerData();
    target_data=event->GetTargetData();

    // If the attacker is no longer in attack mode abort.
    if (gemAttacker->GetMode() != PSCHARACTER_MODE_COMBAT)
    {
#ifdef COMBAT_DEBUG
        psserver->SendSystemError(event->AttackerCID,
                "Combat stopped as you left combat mode.");
#endif
        return;
    }

    // If target is dead, abort.
    if (!gemTarget->IsAlive() )
    {
#ifdef COMBAT_DEBUG
        psserver->SendSystemResult(event->AttackerCID, "Combat stopped as one participant logged of.");
#endif
        return;
    }

    // If the slot is no longer attackable, abort
    if (!attacker_data->Inventory().CanItemAttack(event->GetWeaponSlot()))
    {
#ifdef COMBAT_DEBUG
        psserver->SendSystemError(event->AttackerCID, "Combat stopped as you have no longer an attackable item equipped.");
#endif
        return;
    }
    
    if (attacker_data->Inventory().GetEquipmentObject(event->GetWeaponSlot()).eventId != event->id)
    {
#ifdef COMBAT_DEBUG
        psserver->SendSystemError(event->AttackerCID, "Ignored combat event as newer is in.");
#endif
        return;
    }
   
    psItem* weapon = attacker_data->Inventory().GetEffectiveWeaponInSlot(event->GetWeaponSlot());

    // weapon became unwieldable
    csString response;
    if(weapon!=NULL && !weapon->CheckRequirements(attacker_data,response))
    {
        Debug2(LOG_COMBAT, gemAttacker->GetClientID(),"%s has lost use of weapon", gemAttacker->GetName() );
        psserver->SendSystemError(event->AttackerCID, "You can't use your %s any more.", weapon->GetName() );
        return;
    }

    // If the weapon in the slot has been changed, skip a turn (latency for this slot may also have changed)
    if (event->WeaponID != weapon->GetUID())
    {
        Debug2(LOG_COMBAT, gemAttacker->GetClientID(),"%s has changed weapons mid battle", gemAttacker->GetName() );
#ifdef COMBAT_DEBUG
        psserver->SendSystemError(event->AttackerCID, "Weapon changed. Skipping");
#endif
        skipThisRound = true;
    }

    Client * attacker_client = psserver->GetNetManager()->GetClient(event->AttackerCID);
    if (attacker_client)
    {
        // Input the stamina data
        MathEnvironment env;
        env.Define("Actor",  event->GetAttacker());
        env.Define("Weapon", weapon);
        staminacombat->Evaluate(&env);
        MathVar *PhyDrain = env.Lookup("PhyDrain");
        MathVar *MntDrain = env.Lookup("MntDrain");

        if ( (attacker_client->GetCharacterData()->GetStamina(true) < PhyDrain->GetValue())
            || (attacker_client->GetCharacterData()->GetStamina(false) < MntDrain->GetValue()) )
        {
           StopAttack(attacker_data->GetActor());
           psserver->SendSystemError(event->AttackerCID, "You are too tired to attack.");
           return;
        }

        // If the target has become impervious, abort and give up attacking
        if (!attacker_client->IsAllowedToAttack(gemTarget))
        {
           StopAttack(attacker_data->GetActor());
           return;
        }

        // If the target has changed, abort (assume another combat event has started since we are still in attack mode)
        if (gemTarget != attacker_client->GetTargetObject())
        {
#ifdef COMBAT_DEBUG
            psserver->SendSystemError(event->AttackerCID, "Target changed.");
#endif
            return;
        }
    }
    else
    {
        // Check if the npc's target has changed (if it has, then assume another combat event has started.)
        gemNPC* npcAttacker = dynamic_cast<gemNPC*>(gemAttacker);
        if (npcAttacker && npcAttacker->GetTarget() != gemTarget)
        {
#ifdef COMBAT_DEBUG
            psserver->SendSystemError(event->AttackerCID, "NPC's target changed.");
#endif
            return;
        }
    }

    if (gemAttacker->IsSpellCasting())
    {
        psserver->SendSystemInfo(event->AttackerCID, "You can't attack while casting spells.");
        skipThisRound = true;
    }

    if (!skipThisRound)
    {
        if (weapon->GetIsRangeWeapon() && weapon->GetUsesAmmo())
        {
            INVENTORY_SLOT_NUMBER otherHand = event->GetWeaponSlot() == PSCHARACTER_SLOT_RIGHTHAND ?
                                                                        PSCHARACTER_SLOT_LEFTHAND:
                                                                        PSCHARACTER_SLOT_RIGHTHAND;

            attack_result = ATTACK_NOTCALCULATED;

            psItem* otherItem = attacker_data->Inventory().GetInventoryItem(otherHand);
            if (otherItem == NULL)
            {
                attack_result = ATTACK_OUTOFAMMO;
            }
            else if (otherItem->GetIsContainer()) // Is it a quiver?
            {
                // Pick the first ammo we can shoot from the container
                // And set it as the active ammo
                bool bFound = false;
                for (size_t i=1; i<attacker_data->Inventory().GetInventoryIndexCount() && !bFound; i++)
                {
                    psItem* currItem = attacker_data->Inventory().GetInventoryIndexItem(i);
                    if (currItem && 
                        currItem->GetContainerID() == otherItem->GetUID() &&
                        weapon->GetAmmoTypeID().In(currItem->GetBaseStats()->GetUID()))
                    {
                        otherItem = currItem;
                        bFound = true;
                    }
                }
                if (!bFound)
                    attack_result = ATTACK_OUTOFAMMO;
            }
            else if (!weapon->GetAmmoTypeID().In(otherItem->GetBaseStats()->GetUID()))
            {
                attack_result = ATTACK_OUTOFAMMO;
            }

            if (attack_result != ATTACK_OUTOFAMMO)
            {
                psItem* usedAmmo = attacker_data->Inventory().RemoveItemID(otherItem->GetUID(), 1);
                if (usedAmmo)
                {
                    attack_result=CalculateAttack(event, usedAmmo);
                    usedAmmo->Destroy();
                    psserver->GetCharManager()->UpdateItemViews(attacker_client->GetClientNum());
                }
                else
                    attack_result=CalculateAttack(event);
            }
        }
        else
        {
            attack_result=CalculateAttack(event);
        }

        event->AttackResult=attack_result;

        ApplyCombatEvent(event, attack_result);
    }

    // Queue next event to continue combat if this is an auto attack slot
    if (attacker_data->Inventory().IsItemAutoAttack(event->GetWeaponSlot()))
    {
//      CPrintf(CON_DEBUG, "Queueing Slot %d for %s's next combat event.\n",event->GetWeaponSlot(), event->attacker->GetName() );
        QueueNextEvent(event);
    }
    else
    {
#ifdef COMBAT_DEBUG
        psserver->SendSystemError(event->AttackerCID, "Item %s is not a auto attack item.",attacker_data->Inventory().GetEffectiveWeaponInSlot(event->GetWeaponSlot())->GetName());
#endif
    }
//    else
//        CPrintf(CON_DEBUG, "Slot %d for %s not an auto-attack slot.\n",event->GetWeaponSlot(), event->attacker->GetName() );
}
void CombatManager::AttackSomeone(gemActor *attacker,gemObject *target,Stance stance)
{
    psCharacter *attacker_character = attacker->GetCharacterData();

    //we don't allow an overweight or defeated char to fight
    if (attacker->GetMode() == PSCHARACTER_MODE_DEFEATED || 
        attacker->GetMode() == PSCHARACTER_MODE_OVERWEIGHT)
        return;

    if (attacker->GetMode() == PSCHARACTER_MODE_COMBAT)  // Already fighting
    {
        SetCombat(attacker,stance);  // switch stance from Bloody to Defensive, etc.
        return;
    }
    else
    {
        if (attacker->GetMode() == PSCHARACTER_MODE_SIT) //we are sitting force the char to stand
            attacker->Stand();
        attacker_character->ResetSwings(csGetTicks());
    }

    // Indicator of whether any weapons are available to attack with
    bool startedAttacking=false;
    bool haveWeapon=false;

    // Step through each current slot and queue events for all that can attack
    for (int slot=0; slot<PSCHARACTER_SLOT_BULK1; slot++)
    {
        // See if this slot is able to attack
        if (attacker_character->Inventory().CanItemAttack((INVENTORY_SLOT_NUMBER) slot))
        {
            INVENTORY_SLOT_NUMBER weaponSlot = (INVENTORY_SLOT_NUMBER) slot;
            // Get the data for the "weapon" that is used in this slot
            psItem *weapon=attacker_character->Inventory().GetEffectiveWeaponInSlot(weaponSlot);

            csString response;
            if (weapon!=NULL && weapon->CheckRequirements(attacker_character,response) )
            {
                haveWeapon = true;
                Debug5(LOG_COMBAT,attacker->GetClientID(),"%s tries to attack with %s weapon %s at %.2f range",
                       attacker->GetName(),(weapon->GetIsRangeWeapon()?"range":"melee"),weapon->GetName(),
                       attacker->RangeTo(target,false));
                Debug3(LOG_COMBAT,attacker->GetClientID(),"%s started attacking with %s",attacker->GetName(),
                       weapon->GetName());
                
                // start the ball rolling
                QueueNextEvent(attacker,weaponSlot,target,attacker->GetClientID(),target->GetClientID());  
                
                startedAttacking=true;
            }
            else
            {
                if( weapon  && attacker_character->GetActor())
                {
                    Debug3(LOG_COMBAT,attacker->GetClientID(),"%s tried attacking with %s but can't use it.",
                           attacker->GetName(),weapon->GetName());
#ifdef COMBAT_DEBUG
                    psserver->SendSystemError(attacker_character->GetActor()->GetClientID(), response);
#endif
                } 
            }
        }
    }

    /* Only notify the target if any attacks were able to start.  Otherwise there are
     * no available weapons with which to attack.
     */
    if (haveWeapon)
    {
        if (startedAttacking)
        {
            // The attacker should now enter combat mode
            if (attacker->GetMode() != PSCHARACTER_MODE_COMBAT)
            {
                SetCombat(attacker,stance);
            }
        }
        else
        {
            psserver->SendSystemError(attacker->GetClientID(),"You are too far away to attack!");
            return;
        }
    }
    else
    {
        psserver->SendSystemError(attacker->GetClientID(),"You have no weapons equipped!");
        return;
    }
}