Exemple #1
0
// called when a monster die [give exp/give extra drop]
bool CMonster::OnDie( )
{
    CMap* map = GServer->MapList.Index[Position->Map];
    if(map->ghost!=0)
    {
     if((map->IsNight( ) || map->ghost==2) && !IsGhost( ) && !IsGhostSeed( ) && !IsSummon( ))
       {
        UINT gs = GServer->RandNumber( 0, 100 );
        if(gs<30) // 30% / 70%
            {   // spawn a ghost seed [for now will be 701 [level 1 ghost seed] ]
                map->AddMonster( 701, Position->current, 0, NULL, NULL, Position->respawn, true );
            }
        }
    }
    
    //LMA begin
    //CF mode 1
    //20070621-211100
    UINT special_exp=0;
    UINT special_lvl=0;
    
    if (map->is_cf==1)
    {
       //what monster is dead?
       if(this->montype==map->id_def_mon)
       {
           //oh my god, they killed a j&b !! (or the monster with exp)
           special_exp=(UINT) map->mon_lvl;
           special_lvl= (UINT) map->mon_exp;
       }
       else
       {
           if (this->montype!=map->id_temp_mon)
           {
                UINT gs = GServer->RandNumber( 0, 100 );
                if(gs<map->percent)
                {
                  //we use the temporary monster as a decoy.
                  fPoint position_cf = GServer->RandInCircle( Position->current, 50 );
                  CMonster* monster2 = map->AddMonster( map->id_temp_mon, position_cf, 0, NULL, NULL, 0, true );           
                }
                
           }

       }
       
    }
    
    GServer->GiveExp( this , special_lvl, special_exp );
    //LMA End
    

    return true; 
}
Exemple #2
0
// Respawn a Monster
void CMap::RespawnMonster( )
{
    //PY new test version. Comment out to use older lmame version
    for(UINT j=0;j<MonsterSpawnList.size();j++)
    {
      CSpawnArea* thisspawn = MonsterSpawnList.at(j);
      //check to see if the spawn is currently empty
      if(thisspawn->MCount <= 0 and thisspawn->ResetSpawn == false)
      {
        //set start of spawn timer. We want it to count from the last death, not last spawn.
        thisspawn->lastRespawnTime = clock();
        thisspawn->ResetSpawn = true;
      }
      //check to see if enough time has passed and that the spawn
      clock_t rtime = clock() - thisspawn->lastRespawnTime;
      if (rtime > thisspawn->respawntime*CLOCKS_PER_SEC && thisspawn->MCount <= 0)
      {
        //spawn has been empty for long enough. Now we need to respawn it.
        //first move the pointer to the next bmob or tmob
        //Yes I know I could simplify this sequence but it's much clearer this way.
        if(thisspawn->MobPointer == 6)
        {
          //tactical mobs cleared. reset to basic mobs again
          thisspawn->MobPointer = 1;
        }
        if(thisspawn->MobPointer == 5)
        {
          //reached the end of bmobs. Time to move onto tmobs
          thisspawn->MobPointer = 6;
        }
        if(thisspawn->MobPointer < 5)
        {
          //increment the pointer to the next basic mob
          thisspawn->MobPointer++;
        }
        thisspawn->MCount = 0; //make sure the count is set to zero
        thisspawn->ResetSpawn = false;
        //OK pointer and counter set. now we spawn em
        if(thisspawn->MobPointer > 5) //Do this only if we are spawning tactical mobs
        {
          //tactical mobs. We spawn both groups at the same time. the monster count becomes the combined total
          if(thisspawn->tcount[1] > 0)
          {
            for (UINT k=0;k<thisspawn->tcount[1];k++) //spawn the tactical monsters within this tmob
            {
              fPoint position = GServer->RandInCircle( thisspawn->points, thisspawn->radius );
              AddMonster( thisspawn->tmob[1], position, 0, NULL, NULL, thisspawn->id );
              thisspawn->MCount++;
            }
          }
          if(thisspawn->tcount[2] > 0)
          {
            for (UINT k=0;k<thisspawn->tcount[2];k++) //spawn the tactical monsters within this tmob
            {
              fPoint position = GServer->RandInCircle( thisspawn->points, thisspawn->radius );
              AddMonster( thisspawn->tmob[2], position, 0, NULL, NULL, thisspawn->id );
              thisspawn->MCount++;
            }
          }
          if(thisspawn->tcount[1] == 0 && thisspawn->tcount[2] == 0)
          {
            //reset Mob pointer to 1 if there are no tactical mobs in the spawn
            thisspawn->MobPointer = 1;
          }

        }
        if(thisspawn->MobPointer <6) //basic mobs
        {
          {
            if(thisspawn->bcount[thisspawn->MobPointer] > 0)
            {
              for (UINT k=0;k<thisspawn->bcount[thisspawn->MobPointer];k++) //spawn the basic monsters within this bmob
              {
                fPoint position = GServer->RandInCircle( thisspawn->points, thisspawn->radius );
                AddMonster( thisspawn->bmob[thisspawn->MobPointer], position, 0, NULL, NULL, thisspawn->id );
                thisspawn->MCount++;
              }
            }
            else //bcount for this spawn is zero so reset mob pointer to first spawn or tactical spawn
            {
              if(thisspawn->tcount[1] == 0 && thisspawn->tcount[2] == 0)
              {
                // No tactical spawns so set pointer to 1
                thisspawn->MobPointer = 1;
              }
              else
              {
                // There are tactical spawns so set pointer to 6. Don't think this will ever happen but added it for completeness
                thisspawn->MobPointer = 6;
              }
            }
          }
        }
      }
    }
    return;
    //This kicks it out before it ever runs the following code.
    //if the spawn didn't contain any entries then MCount will still be zero so the pointer will move on next time around
    //Well that was pretty simple to rewrite.
    //Way more elegant that all that convoluted stuff down below ^_^
    //PY END

    //LMA: daynight
    int last_map = 0;
    bool is_night = false;

    bool lma_debug = false;   //LMA: debugging spawn problem

    for (UINT j = 0; j < MobGroupList.size(); j++)
    {
       CMobGroup* thisgroup = MobGroupList.at(j);

       //LMA: debugging spawn problem
       lma_debug = false;
       if(thisgroup->id == 4589)
       {
           lma_debug=true;
           Log(MSG_INFO,"Spawn %u:: Respawn time?",thisgroup->id);
       }

      clock_t rtime = clock() - thisgroup->lastRespawnTime;
      if (rtime < thisgroup->respawntime*CLOCKS_PER_SEC || thisgroup->active >= thisgroup->limit)
      {
          if(lma_debug)
          {
              Log(MSG_INFO,"Spawn %u:: No respawn (time %u < %u ? or limit %u>=%u ?)",thisgroup->id,rtime,thisgroup->respawntime*CLOCKS_PER_SEC,thisgroup->active,thisgroup->limit);
          }

        continue;
      }

      //LMA: handling day and night this time.
      if(thisgroup->daynight!=0)
      {
        if (last_map!=thisgroup->map)
        {
            CMap* map = GServer->MapList.Index[thisgroup->map];
            last_map=map->id;
            is_night=map->IsNight();
        }

        if((thisgroup->daynight == 2 && !is_night) || (thisgroup->daynight == 1 && is_night))
            continue;
      }

      //LMA: new way.
      //calling in tactictal points if needed.
      CMob *thismob;
      bool groupFull = false;

      if(lma_debug)
      {
          Log(MSG_INFO,"Spawn %u:: Tactical? %u>=%u ?",thisgroup->id,thisgroup->basicKills,thisgroup->tacticalpoints);
      }

      if (thisgroup->tacMobs.size() > 0 && thisgroup->basicKills >= thisgroup->tacticalpoints)
      {
          if(lma_debug)
          {
              Log(MSG_INFO,"Spawn %u:: Tactical time",thisgroup->id);
          }

          //adding ALL tacticals.
          for (UINT k=0;k<thisgroup->tacMobs.size();k++)
          {
            if (thisgroup->curTac >= thisgroup->tacMobs.size())
            {
                thisgroup->curTac = 0;
            }

            thismob = thisgroup->tacMobs.at(thisgroup->curTac);
            thisgroup->basicKills = 0;
            thisgroup->lastKills = 0;
            thisgroup->curTac++;

            //we add the monsters.
            for (UINT i = 0; i < thismob->amount; i++)
            {
                //We load an entire group, not relying to active already there.
                /*
                if (thisgroup->active >= thisgroup->limit)
                {
                    groupFull=true;
                    break;
                }
                */

                //adding the monster as tactical.
                fPoint position = GServer->RandInCircle( thisgroup->point, thisgroup->range );
                AddMonster( thismob->mobId, position, 0, thismob->mobdrop, thismob->mapdrop, thisgroup->id,false,true);
            }

            //We load an entire group, not relying to active already there.
            /*
            if(groupFull)
            {
                break;
            }
            */

          }

      }

      //LMA: basic mobs again? we only add one group.
      //we do it only if enough monsters were killed since last time.
      int last_group = thisgroup->curBasic - 1;
      if(last_group < 0)
      {
          last_group = thisgroup->basicMobs.size() - 1;
          if(last_group < 0)
          {
              last_group = 0;
          }

      }

      if(lma_debug)
      {
          Log(MSG_INFO,"Spawn %u:: Basic group check, this one %u, previous %u, laskills %u>=amount %u ?",thisgroup->id,thisgroup->curBasic,last_group,thisgroup->lastKills,thisgroup->basicMobs.at(last_group)->real_amount);
      }

      //if(thisgroup->lastKills>=thisgroup->basicMobs.at(last_group)->amount)
      if(thisgroup->lastKills >= thisgroup->basicMobs.at(last_group)->real_amount)
      {
          if(!thisgroup->group_ready)
          {
              //extra loop since it's the last dead monster that triggers the respawn time.
              thisgroup->lastRespawnTime = clock();
              thisgroup->group_ready = true;

              if(lma_debug)
              {
                  Log(MSG_INFO,"Spawn %u:: Group wasn't ready to spawn, he is now.",thisgroup->id);
              }


              return;
          }

          thisgroup->group_ready=false;

          thismob = thisgroup->basicMobs.at(thisgroup->curBasic);
          thisgroup->curBasic++;
          if (thisgroup->curBasic >= thisgroup->basicMobs.size())
          {
              thisgroup->curBasic = 0;
          }

          if(lma_debug)
          {
              Log(MSG_INFO,"Spawn %u:: new group will be %u, active for now %u",thisgroup->id,thisgroup->curBasic,thisgroup->active);
          }

        //we add the monsters.
        //LMA: Don't spawn all the mobs at once.
        thismob->real_amount = 1;
        if(thismob->amount != 1)
        {
            thismob->real_amount=GServer->RandNumber(1,thismob->amount);
            if(thismob->real_amount>thismob->amount)
            {
                thismob->real_amount=thismob->amount;
            }

        }

      if(lma_debug)
      {
          Log(MSG_INFO,"Spawn %u:: We'll spawn %u monsters (rand from 1 to %u)",thismob->real_amount,thismob->amount);
      }

        thisgroup->lastKills = 0;
        //LMA: setting the respawn time here.
        thisgroup->lastRespawnTime = clock();

        //for (UINT i = 0; i < thismob->amount; i++)
        for (UINT i = 0; i < thismob->real_amount; i++)
        {
            if (thisgroup->active >= thisgroup->limit)
            {
                  if(lma_debug)
                  {
                      Log(MSG_INFO,"Spawn %u:: Stop spawning monster %u>=%u",thisgroup->id,thisgroup->active,thisgroup->limit);
                  }

                break;
            }

            fPoint position = GServer->RandInCircle( thisgroup->point, thisgroup->range );

            //LMA: We get the monster for test purposes.
            //AddMonster( thismob->mobId, position, 0, thismob->mobdrop, thismob->mapdrop, thisgroup->id );
            CMonster* tempmonster=AddMonster( thismob->mobId, position, 0, thismob->mobdrop, thismob->mapdrop, thisgroup->id );

              if(lma_debug)
              {
                  if (tempmonster != NULL)
                  {
                      Log(MSG_INFO,"Spawn %u:: Spawing monster %u (CID %u) at (%.2f;%.2f) (%u<%u), active now %u",thisgroup->id,thismob->mobId,tempmonster->clientid,position.x,position.y,thismob->mobId,i,thismob->real_amount,thisgroup->active);
                  }
                  else
                  {
                      Log(MSG_WARNING,"Spawn %u:: FAILURE Spawing monster %u at (%.2f;%.2f) (%u<%u), active now %u",thisgroup->id,thismob->mobId,position.x,position.y,thismob->mobId,i,thismob->real_amount,thisgroup->active);
                  }

              }

        }

      }

    }


    return;
}
// Map Process
PVOID MapProcess( PVOID TS )
{
    bool ok_cont=false;
    UINT loopcount=0;
    clock_t time_skill=0;
    bool only_npc=false;    //LMA: AIP is done by NPC even when no player in map.
    bool only_summon=false;  //LMA: test for summons when no one's around (sad is a summon's life ^_^).
    UINT nb_summons_map=0;  //LMA: test for summons when no one's around (sad is a summon's life ^_^).

    //LMA: temp monster used for NPCs.
    fPoint tempPos;
    tempPos.x=0;
    tempPos.y=0;
    tempPos.z=0;
    CMonster* NPCmonster = new (nothrow) CMonster( tempPos, 0, 0, 0, 0  );

    while(GServer->ServerOnline)
    {
        loopcount++;            //geobot: refresh only every 100 cycles
        if (loopcount < 100)
        {
            continue;
        }

        loopcount = 0;

        pthread_mutex_lock( &GServer->PlayerMutex );
        pthread_mutex_lock( &GServer->MapMutex );

		

        for(UINT i=0;i<GServer->MapList.Map.size();i++)
        {
            CMap* map = GServer->MapList.Map.at(i);

            //LMA: test for Union.
            only_npc=false;
            only_summon=false;  //LMA: test for summons when no one's around (sad is a summon's life ^_^).
            nb_summons_map=0;
            if( map->PlayerList.size()<1 )
            {
                only_npc = true;    //LMA: AIP is done by NPC even when no player in map.

                //LMA: doing summons too if there are any in map.
                if(map->nb_summons > 0)
                {
                    only_summon = true;
                }
                //continue;
            }

            if (!only_npc||only_summon)
            {
                // Player update //------------------------
                for(UINT j=0;j<map->PlayerList.size();j++)
                {
                    CPlayer* player = map->PlayerList.at(j);
					//check for delayed trigger
					if(player->TriggerDelay != 0 )	//only one problem. If the player logs out before the delayed action trigger is executed than it never will be. Oh Dear! The missing children will really be missing. lol. It's fine though because they never truly despawn. they just disappear from the player's visibility list.
					{
						clock_t etime = clock() - player->DelayStartTime;
						if( etime >= player->TriggerDelay )
						{
							player->ExecuteQuestTrigger(player->TriggerHash,true);
							player->TriggerDelay = 0;	//reset delays and stuff
							player->TriggerHash = 0;
						}
					}
                    if(!player->Session->inGame) continue;

                    if(player->IsDead( ))
                    {
                          player->lastRegenTime = 0;
                          player->lastShowTime = 0;
                          continue;
                     }
                    player->RefreshHPMP();         //LMA HP / MP Jumping
                    if(player->UpdateValues( )) //Does nothing except for rides... equals to true if player isn't on the back seat
                        player->UpdatePosition(false);
                    if(player->IsOnBattle( ))
                        player->DoAttack( );

                    player->RefreshBuff( );
                    player->PlayerHeal( );
                    player->Regeneration( );
                    player->CheckPlayerLevelUP( );

                    player->CheckDoubleEquip(); //LMA: Core fix for double weapon and shield
                    player->CheckZulies( );

                    //Fuel handling.
                    if (player->Status->Stance == DRIVING && (player->last_fuel>0) && (clock()-player->last_fuel > 60000))
                    {
                      //We kill some fuel every now and then :)
                      player->TakeFuel();
                      player->last_fuel=clock();
                    }

                    //LMA: mileage coupon checks.
                    time_t etime=time(NULL);
                    if(player->no_exp&&(etime>=player->timer_no_exp))
                    {
                      BEGINPACKET( pak, 0x702 );
                      ADDSTRING( pak, "[Mileage] Null Xp vanished.");
                      ADDBYTE( pak, 0 );
                      player->client->SendPacket(&pak);
                      player->timer_no_exp=0;
                      player->no_exp=false;
                    }
                    if(player->bonusxp>1&&(etime>=player->timerxp))
                    {
                      BEGINPACKET( pak, 0x702 );
                      ADDSTRING( pak, "[Mileage] Bonus Xp vanished.");
                      ADDBYTE( pak, 0 );
                      player->client->SendPacket(&pak);
                      player->bonusxp=1;
                      player->timerxp=0;
                      player->wait_validation=0;
                    }
                    if(player->bonusddrop>1&&(etime>=player->timerddrop))
                    {
                      BEGINPACKET( pak, 0x702 );
                      ADDSTRING( pak, "[Mileage] Medal of Fortune vanished.");
                      ADDBYTE( pak, 0 );
                      player->client->SendPacket(&pak);
                      player->bonusddrop=1;
                      player->timerddrop=0;
                      player->wait_validation_ddrop=0;
                    }
                    if(player->bonusstatdrop>1&&(etime>=player->timerstatdrop))
                    {
                      BEGINPACKET( pak, 0x702 );
                      ADDSTRING( pak, "[Mileage] Medal of Excellence vanished.");
                      ADDBYTE( pak, 0 );
                      player->client->SendPacket(&pak);
                      player->bonusstatdrop=1;
                      player->timerstatdrop=0;
                      player->wait_validation_statdrop=0;
                    }
                    if(player->bonusgraydrop>0&&(etime>=player->timergraydrop))
                    {
                      BEGINPACKET( pak, 0x702 );
                      ADDSTRING( pak, "[Mileage] Medal of Retrieval vanished.");
                      ADDBYTE( pak, 0 );
                      player->client->SendPacket(&pak);
                      player->bonusgraydrop=0;
                      player->timergraydrop=0;
                      player->wait_validation_graydrop=0;
                    }
                    if(player->Shop->ShopType>0&&(etime>=player->Shop->mil_shop_time))
                    {
                      BEGINPACKET( pak, 0x702 );
                      ADDSTRING( pak, "[Mileage] Mileage shop expired !");
                      ADDBYTE( pak, 0 );
                      player->client->SendPacket(&pak);
                      player->Shop->ShopType=0;
                      player->Shop->mil_shop_time=0;
                    }
                    if(player->Shop->ShopType>0&&(etime>=player->Shop->mil_shop_time))
                    {
                      BEGINPACKET( pak, 0x702 );
                      ADDSTRING( pak, "[Mileage] Mileage shop expired !");
                      ADDBYTE( pak, 0 );
                      player->client->SendPacket(&pak);
                      player->Shop->ShopType=0;
                      player->Shop->mil_shop_time=0;
                    }

                }

                // Monster update //------------------------
                pthread_mutex_lock( &map->MonsterMutex );

                //spawn TD monsters
                unsigned nextmon = map->TDMobList[map->TDNextSpawn];
                if(nextmon != 0)
                {
                    //Log(MSG_DEBUG,"Spawning TD mobs");
                    clock_t etime = clock() - map->lastTDSpawnTime;
                    if(etime >= map->TDMobDelay) // check if elapsed time is greater than spawn delay
                    {
                         //spawn the next monster on the list
                         //Log(MSG_DEBUG,"Spawning TD mob of type %i at position %i in the list for map %i",nextmon, map->TDNextSpawn, map->id);
                         map->AddMonster(nextmon, GServer->WPList[map->id][1].pos, 0, 0, 0, 1, 0, 999 ); //spawn a TD mob with no AI
                         //Log(MSG_DEBUG,"Spawned mob in addmonster");
                         map->TDMobList[map->TDNextSpawn] = 0; //clear the spawn
                         //Log(MSG_DEBUG,"cleared the spawn");
                         map->TDNextSpawn++; //move the pointer
                         //Log(MSG_DEBUG,"moved the pointer");
                         if(map->TDNextSpawn > 100)map->TDNextSpawn = 0;
                         map->lastTDSpawnTime = clock();
                    }
                }
                //TD spawn end

                for(UINT j=0;j<map->MonsterList.size();j++)
                {
                    CMonster* monster = map->MonsterList.at(j);
                    //UINT thistimer = monster->AItimer;
                    clock_t etime = clock() - monster->lastAiUpdate;

                    //LMA: only summon ?
                    if (only_summon&&!monster->IsSummon())
                    {
                        continue;
                    }
                    else if(only_summon)
                    {
                        nb_summons_map++;
                    }

					if(monster->hitcount == 0xFF)//this is a delay for new monster spawns this might olso fix invisible monsters(if they attack directly on spawning the client dosn't get the attack packet(its not in it's visible list yet))
                    {
                        monster->hitcount = 0;
                        //monster->DoAi(monster->thisnpc->AI, 0);
                        monster->DoAi(monster->MonAI, 0);		//AI on spawn
                        monster->lastAiUpdate=clock();
                    }

                    //PY handling day only or night only monsters. 
                    
					if(!map->IsNight( ) && monster->Status->nightonly)// if day, delete all night time monsters
                    {
                        //Log( MSG_INFO, "Night Only monster deleted. Type %i", monster->montype);
                        map->DeleteMonster( monster, true, j );
                        continue;
                    }
                    if(map->IsNight() && monster->Status->dayonly)
                    {
                        //Log( MSG_INFO, "Day Only monster deleted. Type %i", monster->montype);
                        map->DeleteMonster( monster, true, j );
                        continue;
                    }

                    //Do TD stuff
                    if(monster->MonAI == 999 && etime >= monster->AITimer) //It's a TD mob
                    {
                        monster->UpdatePosition(monster->stay_still );
                        float distance = GServer->distance( monster->Position->current, monster->Position->destiny );
                        //Log( MSG_INFO, "Found a TD monster %f from target. Waypoint type = %i stance %i speed %i", distance,GServer->WPList[map->id][monster->NextWayPoint].WPType,monster->Stats->stance, monster->Stats->Move_Speed);
                        //Log( MSG_INFO, "monster location X: %f Y: %f",monster->Position->current.x, monster->Position->current.y);
                        //Log( MSG_INFO, "WP location X: %f Y: %f ", GServer->WPList[map->id][monster->NextWayPoint].pos.x,GServer->WPList[map->id][monster->NextWayPoint].pos.y);
                        if(distance < 1) //monster has reached it's destination
                        {
                            switch(GServer->WPList[map->id][monster->NextWayPoint].WPType)
                            {
                                case 1: //Start waypoint
                                case 2: //regular waypoint. Increment waypoint counter. Set detiny to next waypoint
                                {
                                    //Log( MSG_INFO, "Monster reached waypoint %i type: %i", monster->NextWayPoint,GServer->WPList[map->id][monster->NextWayPoint].WPType);
                                    monster->NextWayPoint++;
                                    monster->Position->destiny.x = GServer->WPList[map->id][monster->NextWayPoint].pos.x;
                                    monster->Position->destiny.y = GServer->WPList[map->id][monster->NextWayPoint].pos.y;
                                    BEGINPACKET( pak, 0x797 );
                                	ADDWORD    ( pak, monster->clientid );
                                	ADDWORD    ( pak, 0x0000 );
                                	ADDWORD    ( pak, monster->Stats->Move_Speed ); //speed
                                	ADDFLOAT   ( pak, monster->Position->destiny.x * 100 );
                                	ADDFLOAT   ( pak, monster->Position->destiny.y * 100 );
                                	ADDWORD    ( pak, 0xcdcd );
                                	ADDBYTE    ( pak, monster->Stats->stance );  //should be walking
                                	GServer->SendToVisible(&pak, monster);
                                }
                                break;
                                case 3: //Final waypoint. do stuff. suicide
                                {
                                    //Log( MSG_INFO, "Reached final waypoint");
                                    //monster->Stats->HP = 0;
                                    map->DeleteMonster( monster, true, j );
                                    //Log( MSG_INFO, "monster successfully deleted");
                                    continue;
                                }
                                break;
                                default:
                                    Log( MSG_INFO, "WPType %i not recognized",GServer->WPList[map->id][monster->NextWayPoint].WPType);
                                break;
                            }
                        }
                        else
                        {
                            //send move packet again to try to synchronise the monster movement
                            BEGINPACKET( pak, 0x797 );
                        	ADDWORD    ( pak, monster->clientid );
                        	ADDWORD    ( pak, 0x0000 );
                        	ADDWORD    ( pak, monster->Stats->Move_Speed ); //speed
                        	ADDFLOAT   ( pak, monster->Position->destiny.x * 100 );
                        	ADDFLOAT   ( pak, monster->Position->destiny.y * 100 );
                        	ADDWORD    ( pak, 0xcdcd );
                        	ADDBYTE    ( pak, monster->Stats->stance );  //should be walking
                        	GServer->SendToVisible(&pak, monster);
                        }
                        monster->lastAiUpdate = clock();
                    }
                    //End TD stuff

                    //if(!monster->PlayerInRange( )) continue;
                    if(!monster->UpdateValues( )) continue;
                    monster->UpdatePosition( true );
                    if(monster->IsOnBattle( ))
                    {
                         monster->DoAttack( );   // why was this commented? Monsters were not attacking
                         //monster->DoAi(monster->thisnpc->AI, 2);  //don't use thisnpc->AI any more.
                         if(etime >= monster->AITimer)
                         {
                             monster->DoAi(monster->MonAI, 2);
                             monster->lastAiUpdate = clock();
                         }
                         //Log(MSG_INFO,"Monster type: %i current HP: %i",monster->montype, monster->Stats->HP);
                    }
                    else
                    {
                         //monster->DoAi(monster->thisnpc->AI, 1);
                         if(etime >= monster->AITimer)
                         {
                            monster->DoAi(monster->MonAI, 1);
                            monster->lastAiUpdate = clock();
                         }
                    }
                    monster->RefreshBuff( );
                    if (monster->IsSummon())
                    {
                        monster->SummonUpdate(monster,map, j);
                        continue;
                    }
                    if(monster->IsDead())
                    {
                        if(clock() - monster->DeathDelayTimer > GServer->Config.DeathDelay)
                        {
                            //Log(MSG_DEBUG,"Found dead monster montype %i",monster->montype);
                            monster->OnDie( );  //all this does is give exp
                            //Log(MSG_DEBUG,"back from giving exp");
                            monster->DoAi(monster->MonAI, 5);
                            //Log(MSG_DEBUG,"ran AI");
                            map->DeleteMonster( monster, true, j );
                            //Log(MSG_DEBUG,"deleted monster");
                            continue;
                        }
                        else
                        {
                            //Log(MSG_DEBUG,"Dead monster found. waiting for death delay timer");
                        }
                    }

                }

                //LMA: was there any summons in this map?
                if(only_summon&&nb_summons_map==0)
                {
                    map->nb_summons=0;
                }

            }

            if(only_npc&&!only_summon)
            {
                pthread_mutex_lock( &map->MonsterMutex );
            }

            //LMA: AIP for NPC.
            for(UINT j=0;j<map->NPCList.size();j++)
            {
                CNPC* npc = map->NPCList.at(j);

                //LMA: We don't worry about IFO Objects...
                if(npc->npctype>10000)
                {
                    continue;
                }

                if(npc->thisnpc->AI != 0 && npc->thisnpc->AI != 30)
                {
                    //check every minute. Conditions seem to be based on 6 minute segments
                    //LMA: untrue for some NPCs, special case for UW...
					//PY: NO we don't check every minute. We check whatever interval the AIP file asks for

                    bool is_time_ok=false;

                    // int delay=60000;    //each AIP 60 seconds.
                    //LMA: AIP Timer.
                    //delay = npc->thisnpc->AiTimer;

                    if(npc->thisnpc->AiTimer == 0)
                    {
						//npc->thisnpc->AiTimer = 60000;
                        //Log(MSG_WARNING,"NPC %i hadn't timer, file AI=%i",npc->npctype,npc->thisnpc->AI);
						//PY: If the NPC doesn't have a timer then let's give it one
						CAip* script = NULL;
						for(unsigned k=0; k < GServer->AipList.size(); k++)
						{
							if (GServer->AipList.at(k)->AInumber == npc->thisnpc->AI)
							{
								script = GServer->AipList.at(k);
								break;
							}
						}
						if(script == NULL)
						{
							Log( MSG_WARNING, "Invalid AI script for AI %i Setting AI to 30 (blank)", npc->thisnpc->AI );
							npc->thisnpc->AI = 30;
							continue;
						}
						//Set the timer
						npc->thisnpc->AiTimer = script->minTime;
                    }
					//PY: Any npc reaching this point has a valid timer. It has either been set or removed
					
                    
					//PY MORE Bloody lmame special cases
					//If he had just set the frickin timer correctly like i did above, all this crap would be un-necessary

					//Leum, for Union War (no need to do his stuff always). //PY: NO this is BS
                    //if(npc->npctype==1113&&GServer->ObjVar[1113][1]>0)
                    //{
                        //LogDebug("Doing an update for Leum each 10 seconds since UW is on");
                    //    delay=10000;
                    //}

                    //PY: What the hell is this shit? commenting it out
					//Walls for map 66 (no need to do his stuff always)
                    //if(npc->npctype>=1024&&npc->npctype<=1027&&GServer->ObjVar[1249][2]>0&&GServer->ObjVar[1249][2]<=90)
                    //{
                        //LogDebug("Doing an update for Wall %i each second quest from Hope is on",npc->npctype);
                    //    delay=1000;
                    //}

                    //PY: Seriously??
					//Hope map 66 (no need to do his stuff always)
                    //if(npc->npctype==1249&&GServer->ObjVar[1249][2]>0&&GServer->ObjVar[1249][2]<=90)
                    //{
                        //LogDebug("Doing an update for Hope each 10 seconds quest from Hope is on",npc->npctype);
                    //    delay=10000;
                    //}

                    //PY and more special cases........
					//Williams
                    //if(npc->npctype==1075)
                    //{
                        //Each 5 minutes
                    //    delay=300000;
                    //}

                    //LMA END


                     //if(60000<(UINT)GServer->round((clock( ) - npc->lastAiUpdate)))
                     //if(is_time_ok)
                     //if(delay<(UINT)GServer->round((clock( ) - npc->lastAiUpdate)))
                     //PY NOPE!! How about we do it right
					UINT thistimer = npc->thisnpc->AiTimer * 1000; //this is always set in seconds in AIP
					if(thistimer<(UINT)GServer->round((clock( ) - npc->lastAiUpdate))) //check AIP conditions when the timer calls for it
					{
                         CNPCData* thisnpc = GServer->GetNPCDataByID( npc->npctype );
                         if(thisnpc == NULL)
                         {
                             Log( MSG_WARNING, "Invalid montype %i", npc->npctype );
                             continue;
                         }

                         //LMA: before this temp monster was created and deleted every time, now we only do it once...
                         //new code, we overwrite the temp monster each time rather than creating / deleting him each time.
                        NPCmonster->Position->source = npc->pos;
                        NPCmonster->Position->current = npc->pos;
                        NPCmonster->Position->destiny = npc->pos;
                         NPCmonster->montype=npc->npctype;
                         NPCmonster->Position->Map=map->id;
                         NPCmonster->Position->lastMoveTime = clock( );
                         NPCmonster->SpawnTime = clock( );
                         NPCmonster->lastSighCheck = clock( );
                         NPCmonster->lastLifeUpdate = time(NULL);

                         //old code:
                         //CMonster* NPCmonster = new (nothrow) CMonster( npc->pos, npc->npctype, map->id, 0, 0  );
                         NPCmonster->aip_npctype=npc->npctype;
                         NPCmonster->aip_clientid=npc->clientid;
                         NPCmonster->thisnpc = thisnpc;

                         int lma_previous_eventID = npc->thisnpc->eventid;
                         //Log(MSG_INFO,"XCIDAIBEGIN NPC %i map %i cid %i",npc->npctype,map->id,npc->clientid);

                         NPCmonster->DoAi(NPCmonster->MonAI, 1);
                         //Log(MSG_INFO,"XCIDAIEND NPC %i map %i cid %i",npc->npctype,map->id,npc->clientid);

                        
						 //PY: AAAAAAAAAGGGGGGGGGGHHHHHHHHHH. NO! just NO!!. Stop with the bloody special cases. Fix the damn core code already!!!!!!!
						 //Williams (temple of Oblivion)
                        /*if(npc->npctype == 1075)
                        {
                            //each 5 minutes
                            //saving values for him
                            if(GServer->LastTempleAccess[0]!=GServer->ObjVar[npc->npctype][0]||GServer->LastTempleAccess[1]!=GServer->ObjVar[npc->npctype][1])
                            {
                                GServer->DB->QExecute("UPDATE list_npcs SET eventid=%i, extra_param=%i WHERE type=1075",GServer->ObjVar[1075][0],GServer->ObjVar[1075][1]);
                                /*Log(MSG_WARNING,"Doing an update for Williams each 5 minutes, values changed (%i->%i, %i->%i)",
                                GServer->LastTempleAccess[0],GServer->ObjVar[npc->npctype][0],
                                GServer->LastTempleAccess[1],GServer->ObjVar[npc->npctype][1]);
                            }*/
                            /*else
                            {
                                Log(MSG_WARNING,"Doing an update for Williams each 5 minutes.");
                            }*/
							/*
                            GServer->LastTempleAccess[0]=GServer->ObjVar[npc->npctype][0];
                            GServer->LastTempleAccess[1]=GServer->ObjVar[npc->npctype][1];
                        }*/

                         
						 //PY: I have absolutely no idea what this bit does. I suspuct it's also a load of bollux but I'm leaving it in for now
						 //ToDo Figure this out and tidy it up

						 //LMA: check if eventID changed, if we do it in AIP conditions / actions, it just fails...
                         if (lma_previous_eventID!=NPCmonster->thisnpc->eventid)
                         {
                            //Log(MSG_WARNING,"(1)Event ID not the same NPC %i from %i to %i in map %i, npc->thisnpc->eventid=%i !",npc->npctype,lma_previous_eventID,NPCmonster->thisnpc->eventid,map->id,npc->thisnpc->eventid);
                            LogDebugPriority(3);
                            LogDebug("(1)Event ID not the same NPC %i from %i to %i in map %i, npc->thisnpc->eventid=%i !",npc->npctype,lma_previous_eventID,NPCmonster->thisnpc->eventid,map->id,npc->thisnpc->eventid);
                            LogDebugPriority(4);
                            npc->thisnpc->eventid=NPCmonster->thisnpc->eventid;
                            npc->event=npc->thisnpc->eventid;
                            //LMA: We have to change the event ID here since we didn't send the clientID :(
                            BEGINPACKET( pak, 0x790 );
                            ADDWORD    ( pak, npc->clientid );
                            ADDWORD    ( pak, npc->thisnpc->eventid );
                            GServer->SendToAllInMap(&pak,map->id);
                         }

                        //LMA: We don't clear anymore, too much hassle :(
                        //We declare the temporary monster just once...
                        /*GServer->ClearClientID(NPCmonster->clientid);
                         delete NPCmonster;*/
                         npc->lastAiUpdate = clock();
                     }

                }

                //LMA: Sometimes another NPC does the job for you.
                if(npc->thisnpc->eventid!=GServer->ObjVar[npc->npctype][0])
                {
                    int new_event_id=GServer->ObjVar[npc->npctype][0];
                    LogDebugPriority(3);
                    //Log(MSG_WARNING,"(2)Event ID not the same NPC %i from %i to %i in map %i, npc->thisnpc->eventid=%i !",npc->npctype,npc->thisnpc->eventid,new_event_id,map->id,npc->thisnpc->eventid);
                    LogDebug("(2)Event ID not the same NPC %i from %i to %i in map %i, npc->thisnpc->eventid=%i !",npc->npctype,npc->thisnpc->eventid,new_event_id,map->id,npc->thisnpc->eventid);
                    LogDebugPriority(4);
                    npc->thisnpc->eventid=new_event_id;
                    npc->event=new_event_id;
                    //LMA: We have to change the event ID here since we didn't send the clientID :(
                    BEGINPACKET( pak, 0x790 );
                    ADDWORD    ( pak, npc->clientid );
                    ADDWORD    ( pak, npc->thisnpc->eventid );
                    GServer->SendToAllInMap(&pak,map->id);
                }

            }

            pthread_mutex_unlock( &map->MonsterMutex );
        }

        pthread_mutex_unlock( &GServer->MapMutex );
        pthread_mutex_unlock( &GServer->PlayerMutex );

        #ifdef _WIN32
        Sleep(GServer->Config.MapDelay);
        #else
        usleep(GServer->Config.MapDelay);
        #endif
    }
    pthread_exit( NULL );

    //LMA: we delete the temporary monster.
    GServer->ClearClientID(NPCmonster->clientid);
    delete NPCmonster;
	return 0;
}
Exemple #4
0
// Map Process
PVOID MapProcess( PVOID TS )
{
    bool only_npc = false;
    while(GServer->ServerOnline)
    {
        pthread_mutex_lock( &GServer->PlayerMutex );
        pthread_mutex_lock( &GServer->MapMutex );
        for(UINT i=0;i<GServer->MapList.Map.size();i++)
        {
            CMap* map = GServer->MapList.Map.at(i);
            if( map->PlayerList.size()<1 )
                only_npc = true;
            else
                only_npc = false;
                //continue;
            if(!only_npc)
            {
                // Player update //------------------------
                for(UINT j=0;j<map->PlayerList.size();j++)
                {
                    CPlayer* player = map->PlayerList.at(j);
                    if(!player->Session->inGame) continue;
                    if(player->IsDead( )) continue;
                    //if(player->UpdateValues( ))
                        player->UpdatePosition( );
                    if(player->IsOnBattle( ))
                        player->DoAttack( );
                    player->RefreshBuff( );
                    player->PlayerHeal( );
                    player->Regeneration( );
                    player->CheckPlayerLevelUP( );
                    if( GServer->Config.AUTOSAVE == 1 )
                    {
                        clock_t etime = clock() - player->lastSaveTime;
                        if( etime >= GServer->Config.SAVETIME*1000 )
                        {
                            player->savedata( );
                            player->lastSaveTime = clock();
                        }
                    }
                }
                // Monster update //------------------------
                pthread_mutex_lock( &map->MonsterMutex );
                for(UINT j=0;j<map->MonsterList.size();j++)
                {
                    CMonster* monster = map->MonsterList.at(j);
                    
                    UINT thistimer = monster->AItimer; 
    				if(monster->hitcount == 0xFF)//this is a delay for new monster spawns this might olso fix invisible monsters(if they attack directly on spawning the client dosn't get the attack packet(its not in it's visible list yet))
                    {
                        if(thistimer < (UINT)GServer->round((clock( ) - monster->lastAiUpdate)))
                        {
                            monster->hitcount = 0;
                            //monster->DoAi(monster->thisnpc->AI, 0);
                            monster->DoAi(monster->monAI, 0);
                            monster->lastAiUpdate=clock();
                        }
                    }
                    //still need this for special spawns
                    if(!map->IsNight( ) && monster->Status->nightonly)// if day, delete all night time monsters
                    {
                        //Log( MSG_INFO, "Night Only monster deleted. Type %i", monster->montype);
                        map->DeleteMonster( monster, true, j );
                        continue;
                    }
                    if(map->IsNight() && monster->Status->dayonly)
                    {
                        //Log( MSG_INFO, "Day Only monster deleted. Type %i", monster->montype);
                        map->DeleteMonster( monster, true, j );
                        continue;
                    }
    
                    if(!monster->PlayerInRange( )) continue;
                    if(!monster->UpdateValues( )) continue;
                        monster->UpdatePosition( );
                    if(monster->IsOnBattle( ))
                    {
                        //monster->DoAttack( );
                        if(thistimer<(UINT)GServer->round((clock( ) - monster->lastAiUpdate)))
                        {
                             //monster->DoAi(monster->thisnpc->AI, 2);
                             monster->DoAi(monster->monAI, 2);
                             monster->lastAiUpdate = clock();
                             //Log(MSG_INFO,"Monster type: %i current HP: %i",monster->montype, monster->Stats->HP);
                        }
                        else
                        {
                             //Log(MSG_INFO,"Monster doing attack");
                             monster->DoAttack( );
                        }
                    }
                    else if(!monster->IsOnBattle() && !monster->IsDead( ))
                    {
                        if(thistimer<(UINT)GServer->round((clock( ) - monster->lastAiUpdate)))
                        {
                            //monster->DoAi(monster->thisnpc->AI, 1);
                            monster->DoAi(monster->monAI, 1);
                            monster->lastAiUpdate = clock();
                        }
                    }
                    monster->RefreshBuff( );
                    if (monster->IsSummon())
                    {
                        monster->SummonUpdate(monster,map, j);
                        continue;
                    }
                    if(monster->IsDead( ))
                    {
                        //monster->DoAi(monster->thisnpc->AI, 5);
                        monster->DoAi(monster->monAI, 5);
                        monster->OnDie( );
                    }
                }
            }
            if(only_npc)
                pthread_mutex_lock( &map->MonsterMutex );
            
            //AIP for NPCs
            for(UINT j=0;j<map->NPCList.size();j++)
            {
                CNPC* npc = map->NPCList.at(j);
                if(npc->thisnpc->AI != 0)
                {
                     CAip* script = NULL;
                     for(unsigned j=0; j < GServer->AipList.size(); j++)
                     {
                         if (GServer->AipList.at(j)->AInumber == npc->thisnpc->AI)
                         {
                             script = GServer->AipList.at(j);
                             break;
                         }
                     }
                     if(script == NULL)
                     {
                         //Log( MSG_WARNING, "Invalid AI script for AI %i", npc->thisnpc->AI );
                         continue;
                     }
                     UINT thistimer = script->minTime * 1000; //seems to be set in seconds in AIP
                     if(thistimer<(UINT)GServer->round((clock( ) - npc->lastAiUpdate))) //check AIP conditions when the timer calls for it
                     {
                         CNPCData* thisnpc = GServer->GetNPCDataByID( npc->npctype );
                         if(thisnpc == NULL)
                         {
                             Log( MSG_WARNING, "Invalid montype %i", npc->npctype );
                             continue;
                         }
                         CMonster* monster = new (nothrow) CMonster( npc->pos, npc->npctype, map->id, 0, 0  );
                         monster->thisnpc = thisnpc;
                         monster->CharType = 4;
                         int AIP = monster->thisnpc->AI;
                         int lma_previous_eventID = npc->thisnpc->eventid;
                         monster->DoAi(monster->thisnpc->AI, 1);
                         //check if eventID changed, if we do it in AIP conditions / actions, it just fails...
                         if (lma_previous_eventID != npc->thisnpc->eventid)
                         {
                             //Log(MSG_WARNING,"Event ID not the same NPC %i from %i to %i in map %i, npc->thisnpc->eventid=%i !", npc->npctype, lma_previous_eventID, monster->thisnpc->eventid, map->id, npc->thisnpc->eventid);
                             npc->thisnpc->eventid = monster->thisnpc->eventid;
                             BEGINPACKET( pak, 0x790 );
                             ADDWORD    ( pak, npc->clientid );
                             ADDWORD    ( pak, npc->thisnpc->eventid );
                             GServer->SendToMap(&pak, map->id);
                         }


                         if(AIP == GServer->Config.AIWatch)
                             Log(MSG_DEBUG,"NPC AI number %i successfully run",monster->thisnpc->AI);
                         if(monster->IsOnBattle())
                             monster->DoAttack( );
                         map->DeleteMonster(monster);
                         //delete monster;
                         npc->lastAiUpdate = clock();
                         if(AIP == GServer->Config.AIWatch)
                             Log(MSG_DEBUG,"LastAiUpdate clock reset");
                     }
                }

                //LMA: Sometimes another NPC does the job for you.
                if(npc->thisnpc->eventid != GServer->ObjVar[npc->npctype][0])
                {
                    int new_event_id = GServer->ObjVar[npc->npctype][0];
                    //Log(MSG_WARNING,"(2)Event ID not the same NPC %i from %i to %i in map %i, npc->thisnpc->eventid=%i !",npc->npctype,npc->thisnpc->eventid,new_event_id,map->id,npc->thisnpc->eventid);
                    npc->thisnpc->eventid = new_event_id;
                    //LMA: We have to change the event ID here since we didn't send the clientID :(
                    BEGINPACKET( pak, 0x790 );
                    ADDWORD    ( pak, npc->clientid );
                    ADDWORD    ( pak, npc->thisnpc->eventid );
                    GServer->SendToMap(&pak,map->id);
                }
            }
            pthread_mutex_unlock( &map->MonsterMutex );
        }
        pthread_mutex_unlock( &GServer->MapMutex );
        pthread_mutex_unlock( &GServer->PlayerMutex );
        #ifdef _WIN32
        Sleep(GServer->Config.MapDelay);
        #else
        usleep(GServer->Config.MapDelay);
        #endif
    }
    pthread_exit( NULL );
}