void CombatManager::QueueNextEvent(psCombatGameEvent *event) { QueueNextEvent(event->GetAttacker(), event->GetWeaponSlot(), event->GetTarget(), event->GetAttackerID(), event->GetTargetID(), event->GetAttackResult()); }
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); } }
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); }
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; } }