Exemplo n.º 1
0
void monsterthink(void) {
  if (m_dmsp && spawnremain && lastmillis()>nextmonster) {
    if (spawnremain--==monstertotal)
      con::out("The invasion has begun!");
    nextmonster = lastmillis()+1000;
    spawnmonster();
  }

  if (monstertotal && !spawnremain && numkilled==monstertotal)
    endsp(true);

  // equivalent of player entity touch, but only teleports are used
  loopv(ents) {
    entity &e = ents[i];
    if (e.type!=TELEPORT) continue;
    vec3f v(float(e.x), float(e.y), 0.f);
    loopv(monsters) if (monsters[i]->state==CS_DEAD) {
      if (lastmillis()-monsters[i]->lastaction<2000) {
        monsters[i]->move = 0;
        physics::moveplayer(monsters[i], 1, false);
      }
    } else {
      v.z += monsters[i]->eyeheight;
      const float dist = distance(monsters[i]->o, v);
      v.z -= monsters[i]->eyeheight;
      if (dist<4) game::teleport((int)(&e-&ents[0]), monsters[i]);
    }
  }
  loopv(monsters) if (monsters[i]->state==CS_ALIVE)
    monsteraction(monsters[i]);
}
Exemplo n.º 2
0
void arenarespawn() {
  if (arenarespawnwait) {
    if (arenarespawnwait<lastmillis()) {
      arenarespawnwait = 0;
      con::out("new round starting... fight!");
      respawnself();
    }
  } else if (arenadetectwait==0 || arenadetectwait<lastmillis()) {
    arenadetectwait = 0;
    int alive = 0, dead = 0;
    char *lastteam = NULL;
    bool oneteam = true;
    loopv(players) if (players[i])
      arenacount(players[i], alive, dead, lastteam, oneteam);
    arenacount(player1, alive, dead, lastteam, oneteam);
    if (dead>0 && (alive<=1 || (m_teammode && oneteam))) {
      con::out("arena round is over! next round in 5 seconds...");
      if (alive)
        con::out("team %s is last man standing", lastteam);
      else
        con::out("everyone died!");
      arenarespawnwait = int(lastmillis())+5000;
      arenadetectwait = int(lastmillis()) + 10000;
      player1->ypr.z = 0.f ;
    }
  }
Exemplo n.º 3
0
void pickup(int n, game::dynent *d) {
  int np = 1;
  loopv(players) if (players[i]) np++;
  np = np<3 ? 4 : (np>4 ? 2 : 3);         // spawn times are dependent on number of players
  int ammo = np*2;
  switch (ents[n].type) {
    case I_SHELLS:  additem(n, d->ammo[1], ammo); break;
    case I_BULLETS: additem(n, d->ammo[2], ammo); break;
    case I_ROCKETS: additem(n, d->ammo[3], ammo); break;
    case I_ROUNDS:  additem(n, d->ammo[4], ammo); break;
    case I_HEALTH:  additem(n, d->health,  np*5); break;
    case I_BOOST:   additem(n, d->health,  60);   break;
    case I_GREENARMOUR:
      // (100h/100g only absorbs 166 damage)
      if (d->armourtype==A_YELLOW && d->armour>66) break;
      additem(n, d->armour, 20);
    break;
    case I_YELLOWARMOUR:
      additem(n, d->armour, 20);
    break;
    case I_QUAD:
      additem(n, d->quadmillis, 60);
    break;
    case CARROT:
      ents[n].spawned = false;
      triggertime = int(lastmillis());
      // TODO world::trigger(ents[n].attr1, ents[n].attr2, false);  // needs to go over server for client::multiplayer
    break;
    case TELEPORT: {
      static int lastteleport = 0;
      if (lastmillis()-lastteleport<500) break;
      lastteleport = int(lastmillis());
      teleport(n, d);
    }
    break;
    case JUMPPAD: {
      static int lastjumppad = 0;
      if (lastmillis()-lastjumppad<300) break;
      lastjumppad = int(lastmillis());
      vec3f v((int)(char)ents[n].attr3/10.0f, (int)(char)ents[n].attr2/10.0f, ents[n].attr1/10.0f);
      player1->vel.z = 0;
      player1->vel += v;
      sound::playc(sound::JUMPPAD);
    }
    break;
  }
}
Exemplo n.º 4
0
// called after map start of when toggling edit mode to reset/spawn all monsters
// to initial state
void monsterclear(void) {
  cleanmonsters();
  if (m_dmsp) {
    nextmonster = mtimestart = int(lastmillis())+1000;
    monstertotal = spawnremain = 1; // XXX mode()<0 ? skill*10 : 0;
  } else if (m_classicsp) {
    mtimestart = lastmillis();
    loopv(ents) if (ents[i].type==MONSTER) {
      auto m = basicmonster(ents[i].attr2, ents[i].attr1, M_SLEEP, 100, 0);
      m->o.x = ents[i].x;
      m->o.y = ents[i].y;
      m->o.z = ents[i].z;
      entinmap(m);
      monstertotal++;
    }
  }
}
Exemplo n.º 5
0
dynent *newdynent() {
  dynent *d = (dynent*) MALLOC(sizeof(dynent));
  d->o = zero;
  d->ypr = vec3f(270.f,0.f,0.f);
  d->maxspeed = 22.f;
  d->outsidemap = false;
  d->inwater = false;
  d->radius = 0.5f;
  d->o.y = d->eyeheight = 1.8f;
  d->aboveeye = 0.2f;
  d->frags = 0;
  d->plag = 0;
  d->ping = 0;
  d->lastupdate = int(lastmillis());
  d->enemy = NULL;
  d->monsterstate = 0;
  d->name[0] = d->team[0] = 0;
  d->blocked = false;
  d->lifesequence = 0;
  d->state = CS_ALIVE;
  spawnstate(d);
  return d;
}
Exemplo n.º 6
0
void monsterpain(dynent *m, int damage, dynent *d) {
  if (d->monsterstate) { // a monster hit us
    if (m!=d) { // guard for RL guys shooting themselves :)
      m->anger++; // don't attack straight away, first get angry
      int anger = m->mtype==d->mtype ? m->anger/2 : m->anger;
      if (anger>=monstertypes[m->mtype].loyalty)
        m->enemy = d; // monster infight if very angry
    }
  } else { // player hit us
    m->anger = 0;
    m->enemy = d;
  }
  transition(m, M_PAIN, 0, monstertypes[m->mtype].pain,200);      // in this state monster won't attack
  if ((m->health -= damage)<=0) {
    m->state = CS_DEAD;
    m->lastaction = lastmillis();
    player1->frags = ++numkilled;
    sound::play(monstertypes[m->mtype].diesound, &m->o);
    int remain = monstertotal-numkilled;
    if (remain>0 && remain<=5) con::out("only %d monster(s) remaining", remain);
  } else
    sound::play(monstertypes[m->mtype].painsound, &m->o);
}
Exemplo n.º 7
0
void endsp(bool allkilled) {
  con::out(allkilled ? "you have cleared the map!" : "you reached the exit!");
  con::out("score: %d kills in %d seconds", numkilled, (lastmillis()-mtimestart)/1000);
  monstertotal = 0;
  server::startintermission();
}
Exemplo n.º 8
0
// main AI thinking routine, called every frame for every monster
void monsteraction(dynent *m) {
  if (m->enemy->state==CS_DEAD) {
    m->enemy = player1;
    m->anger = 0;
  }
  normalise(m, m->targetyaw);
  if (m->targetyaw>m->ypr.x) { // slowly turn monster towards his target
    m->ypr.x += curtime()*0.5f;
    if (m->targetyaw<m->ypr.x) m->ypr.x = m->targetyaw;
  } else {
    m->ypr.x -= curtime()*0.5f;
    if (m->targetyaw>m->ypr.x) m->ypr.x = m->targetyaw;
  }

  const float disttoenemy = distance(m->o, m->enemy->o);
  m->ypr.y = atan2(m->enemy->o.y-m->o.y, disttoenemy)*180.f/float(pi);

  // special case: if we run into scenery
  if (m->blocked) {
    m->blocked = false;
    // try to jump over obstackle (rare)
    if (!rnd(20000/monstertypes[m->mtype].speed))
      m->jumpnext = true;
    // search for a way around (common)
    else if (m->trigger<lastmillis() && (m->monsterstate!=M_HOME || !rnd(5))) {
      m->targetyaw += 180.f+rnd(180); // patented "random walk" AI pathfinding (tm) ;)
      transition(m, M_SEARCH, 1, 400, 1000);
    }
  }

  const auto enemyyaw = -atan2(m->enemy->o.x-m->o.x, m->enemy->o.z-m->o.z)/float(pi)*180.f+180.f;

  switch (m->monsterstate) {
    case M_PAIN:
    case M_ATTACKING:
    case M_SEARCH:
      if (m->trigger<lastmillis()) transition(m, M_HOME, 1, 100, 200);
    break;
    // state classic sp monster start in, wait for visual contact
    case M_SLEEP: {
      vec3f target;
      // skip running physics
      if (edit::mode() || !enemylos(m, target)) return;
      normalise(m, enemyyaw);
      const auto angle = abs(enemyyaw-m->ypr.x);
      if (disttoenemy<8.f ||  // the better the angle to the player
         (disttoenemy<16.f && angle<135.f) || // the further the monster can
         (disttoenemy<32.f && angle<90.f) ||  // see/hear
         (disttoenemy<64.f && angle<45.f) ||
         angle<10) {
        transition(m, M_HOME, 1, 500, 200);
        sound::play(sound::GRUNT1+rnd(2), &m->o);
      }
    }
    break;

    // this state is the delay between wanting to shoot and actually firing
    case M_AIMING:
      if (m->trigger<lastmillis()) {
        m->lastaction = 0;
        m->attacking = true;
        shoot(m, m->attacktarget);
        transition(m, M_ATTACKING, 0, 600, 0);
      }
    break;

    // monster has visual contact, heads straight for player and may want to
    // shoot at any time
    case M_HOME:
      m->targetyaw = enemyyaw;
      if (m->trigger<lastmillis()) {
        vec3f target;
        // no visual contact anymore, let monster get as close as possible then
        // search for player
        if (!enemylos(m, target))
          transition(m, M_HOME, 1, 800, 500);
        else  { // the closer the monster is the more likely he wants to shoot
          if (!rnd((int)disttoenemy/3+1) && m->enemy->state==CS_ALIVE) { // get ready to fire
            m->attacktarget = target;
            transition(m, M_AIMING, 0, monstertypes[m->mtype].lag, 10);
          } else // track player some more
            transition(m, M_HOME, 1, monstertypes[m->mtype].rate, 0);
        }
      }
    break;
  }
  physics::moveplayer(m, 1, false); // use physics to move monster
}
Exemplo n.º 9
0
// monster AI is sequenced using transitions: they are in a particular state
// where they execute a particular behaviour until the trigger time is hit, and
// then they reevaluate their situation based on the current state, the
// environment etc., and transition to the next state. Transition timeframes are
// parametrized by difficulty level (skill), faster transitions means quicker
// decision making means tougher AI.
// n = at skill 0, n/2 = at skill 10, r = added random factor
void transition(dynent *m, int state, int moving, int n, int r) {
  m->monsterstate = state;
  m->move = moving;
  n = n*130/100;
  m->trigger = lastmillis()+n-skill*(n/16)+rnd(r+1);
}