void slimeDie(Entity *my) { Entity *entity; int c = 0; for( c=0; c<5; c++ ) { Entity *gib = spawnGib(my); serverSpawnGibForClient(gib); } if (spawn_blood) { int x, y; x = std::min<unsigned int>(std::max<int>(0,my->x/16),map.width-1); y = std::min<unsigned int>(std::max<int>(0,my->y/16),map.height-1); if( map.tiles[y*MAPLAYERS+x*MAPLAYERS*map.height] ) { if( !checkObstacle(my->x,my->y,my,NULL) ) { if( my->sprite == 210 ) entity = newEntity(212,1,map.entities); else entity = newEntity(214,1,map.entities); entity->x = my->x; entity->y = my->y; entity->z = 7.4+(rand()%20)/100.f; entity->parent = my->uid; entity->sizex = 2; entity->sizey = 2; entity->yaw = (rand()%360)*PI/180.0; entity->flags[UPDATENEEDED] = TRUE; entity->flags[PASSABLE] = TRUE; } } } playSoundEntity(my, 69, 64); list_RemoveNode(my->mynode); return; }
void actWallBuilder(Entity *my) { int c; if( !my->skill[28] ) return; // received on signal if( my->skill[28] == 2) { playSoundEntity( my, 182, 64 ); Uint16 x = std::min<Uint16>(std::max(0.0,my->x/16),map.width-1); Uint16 y = std::min<Uint16>(std::max(0.0,my->y/16),map.height-1); map.tiles[OBSTACLELAYER+y*MAPLAYERS+x*MAPLAYERS*map.height] = map.tiles[y*MAPLAYERS+x*MAPLAYERS*map.height]; if( multiplayer==SERVER ) { for( c=0; c<MAXPLAYERS; c++ ) { if( client_disconnected[c]==TRUE ) continue; strcpy((char *)net_packet->data,"WALC"); SDLNet_Write16(x,&net_packet->data[4]); SDLNet_Write16(y,&net_packet->data[6]); net_packet->address.host = net_clients[c-1].host; net_packet->address.port = net_clients[c-1].port; net_packet->len = 8; sendPacketSafe(net_sock, -1, net_packet, c-1); } } list_RemoveNode(my->mynode); } }
void Entity::actSoundSource() { #ifdef SOUND if ( multiplayer == CLIENT ) { return; } if ( soundSourceDelay > 0 && soundSourceDelayCounter == 0 ) { soundSourceDelayCounter = soundSourceDelay; } if ( circuit_status == CIRCUIT_ON ) { // received power if ( soundSourceDelayCounter > 0 ) { --soundSourceDelayCounter; if ( soundSourceDelayCounter != 0 ) { return; } } if ( !soundSourceFired ) { soundSourceFired = 1; if ( soundSourceToPlay >= 0 && soundSourceToPlay < numsounds ) { if ( soundSourceOrigin == 1 ) { for ( int c = 0; c < MAXPLAYERS; ++c ) { playSoundPlayer(c, soundSourceToPlay, soundSourceVolume); } } else { playSoundEntity(this, soundSourceToPlay, soundSourceVolume); } } } } else if ( circuit_status == CIRCUIT_OFF ) { if ( soundSourceDelay > 0 ) { soundSourceDelayCounter = soundSourceDelay; } if ( soundSourceFired && !soundSourceLatchOn ) { soundSourceFired = 0; } } #endif // SOUND }
void fireOffSpellAnimation(spellcasting_animation_manager_t* animation_manager, Uint32 caster_uid, spell_t *spell) { //This function triggers the spellcasting animation and sets up everything. if (!animation_manager) { return; } Entity *caster = uidToEntity(caster_uid); if (!caster) { return; } if (!spell) { return; } if (!magicLeftHand) { return; } if (!magicRightHand) { return; } playSoundEntity(caster, 170, 128 ); stat_t *stat = caster->getStats(); //Save these two very important pieces of data. animation_manager->caster = caster->uid; animation_manager->spell = spell; animation_manager->active = TRUE; animation_manager->stage = CIRCLE; //Make the HUDWEAPON disappear, or somesuch? magicLeftHand->flags[INVISIBLE] = FALSE; magicRightHand->flags[INVISIBLE] = FALSE; animation_manager->lefthand_angle = 0; animation_manager->lefthand_movex = 0; animation_manager->lefthand_movey = 0; animation_manager->circle_count = 0; animation_manager->times_to_circle = (getCostOfSpell(spell) / 10) + 1; //Circle once for every 10 mana the spell costs. animation_manager->mana_left = getCostOfSpell(spell); if (stat->PROFICIENCIES[PRO_SPELLCASTING] < SPELLCASTING_BEGINNER) { //There's a chance that caster is newer to magic (and thus takes longer to cast a spell). int chance = rand()%10; if (chance >= stat->PROFICIENCIES[PRO_SPELLCASTING]/15) { int amount = (rand()%50)/std::max(stat->PROFICIENCIES[PRO_SPELLCASTING]+statGetINT(stat),1); amount = std::min(amount, CASTING_EXTRA_TIMES_CAP); animation_manager->times_to_circle += amount; } } animation_manager->consume_interval = (animation_manager->times_to_circle * ((2 * PI) / HANDMAGIC_CIRCLE_SPEED)) / getCostOfSpell(spell); animation_manager->consume_timer = animation_manager->consume_interval; }
void skeletonDie(Entity *my) { node_t *node, *nextnode; int i = 0; for( node=my->children.first; node!=NULL; node=nextnode ) { nextnode = node->next; if( node->element != NULL && i >= 2 ) { Entity *entity=(Entity *)node->element; if( entity->light != NULL ) list_RemoveNode(entity->light->node); entity->light = NULL; list_RemoveNode(entity->mynode); } list_RemoveNode(node); i++; } int c; for( c=0; c<6; c++ ) { Entity *entity = spawnGib(my); if( entity ) { switch( c ) { case 0: entity->sprite = 229; break; case 1: entity->sprite = 230; break; case 2: entity->sprite = 231; break; case 3: entity->sprite = 233; break; case 4: entity->sprite = 235; break; case 5: entity->sprite = 236; break; } serverSpawnGibForClient(entity); } } playSoundEntity(my, 94, 128); list_RemoveNode(my->mynode); return; }
void humanDie(Entity* my) { int c; for ( c = 0; c < 5; c++ ) { Entity* gib = spawnGib(my); serverSpawnGibForClient(gib); } my->spawnBlood(); playSoundEntity(my, 28, 128); my->removeMonsterDeathNodes(); list_RemoveNode(my->mynode); return; }
void gnomeDie(Entity *my) { node_t *node, *nextnode; int c; for( c=0; c<6; c++ ) { Entity *entity = spawnGib(my); if( entity ) serverSpawnGibForClient(entity); } if (spawn_blood) { int x, y; x = std::min<unsigned int>(std::max<int>(0,my->x/16),map.width-1); y = std::min<unsigned int>(std::max<int>(0,my->y/16),map.height-1); if( map.tiles[y*MAPLAYERS+x*MAPLAYERS*map.height] ) { if( !checkObstacle(my->x,my->y,my,NULL) ) { Entity *entity = newEntity(160,1,map.entities); entity->x = my->x; entity->y = my->y; entity->z = 7.4+(rand()%20)/100.f; entity->parent = my->uid; entity->sizex = 2; entity->sizey = 2; entity->yaw = (rand()%360)*PI/180.0; entity->flags[UPDATENEEDED] = TRUE; entity->flags[PASSABLE] = TRUE; } } } int i = 0; for( node=my->children.first; node!=NULL; node=nextnode ) { nextnode = node->next; if( node->element != NULL && i >= 2 ) { Entity *entity=(Entity *)node->element; if( entity->light != NULL ) list_RemoveNode(entity->light->node); entity->light = NULL; list_RemoveNode(entity->mynode); } list_RemoveNode(node); i++; } playSoundEntity(my,225+rand()%4,128); list_RemoveNode(my->mynode); return; }
void actGate(Entity *my) { int i; if( multiplayer!=CLIENT ) { if (!my->skill[28]) return; //Gate needs the mechanism powered state variable to be set. if (my->skill[28] == 2) { //Raise gate if it's closed. if (!GATE_STATUS) { GATE_STATUS = 1; playSoundEntity(my, 81, 64); serverUpdateEntitySkill(my,3); } } else { //Close gate if it's open. if (GATE_STATUS) { GATE_STATUS = 0; playSoundEntity(my, 82, 64); serverUpdateEntitySkill(my,3); } } } else { my->flags[NOUPDATE] = TRUE; } if (!GATE_INIT ) { GATE_INIT = 1; GATE_STARTHEIGHT = my->z; my->scalex = 1.01; my->scaley = 1.01; my->scalez = 1.01; } // rightclick message if( multiplayer!=CLIENT ) { for(i=0;i<MAXPLAYERS;i++) { if( (i==0 && selectedEntity==my) || (client_selected[i]==my) ) { if(inrange[i]) { messagePlayer(i,language[475]); } } } } if( !GATE_STATUS ) { //Closing gate. if( my->z < GATE_STARTHEIGHT ) { GATE_VELZ += .25; my->z = std::min(GATE_STARTHEIGHT, my->z + GATE_VELZ); } else { GATE_VELZ = 0; } } else { //Opening gate. if( my->z > GATE_STARTHEIGHT-12 ) { my->z = std::max(GATE_STARTHEIGHT-12, my->z - 0.25); // rattle the gate GATE_RATTLE = (GATE_RATTLE==0); if( GATE_RATTLE ) { my->x += .05; my->y += .05; } else { my->x -= .05; my->y -= .05; } } else { // reset the gate's position if( GATE_RATTLE ) { GATE_RATTLE = 0; my->x -= .05; my->y -= .05; } } } //Setting collision node_t *node; bool somebodyinside = FALSE; if( my->z > GATE_STARTHEIGHT-6 && my->flags[PASSABLE] ) { for( node=map.entities->first; node!=NULL; node=node->next ) { Entity *entity = (Entity *)node->element; if( entity==my || entity->flags[PASSABLE] || entity->sprite == 1 ) continue; if( entityInsideEntity(my,entity) ) { somebodyinside = TRUE; break; } } if( !somebodyinside ) my->flags[PASSABLE] = FALSE; } else if( my->z < GATE_STARTHEIGHT-9 && !my->flags[PASSABLE] ) { my->flags[PASSABLE] = TRUE; } }
void actFountain(Entity* my) { Entity* entity; //messagePlayer(0, "actFountain()"); //TODO: Temporary mechanism testing code. /* if( multiplayer != CLIENT ) { if (my->skill[28]) { //All it does is change its sprite to sink if it's powered. if (my->skill[28] == 1) { my->sprite = 163; } else { my->sprite = 164; } } }*/ //****************END TEST CODE*************** //TODO: Sounds. // spray water if ( my->skill[0] > 0 ) { #define FOUNTAIN_AMBIENCE my->skill[7] FOUNTAIN_AMBIENCE--; if ( FOUNTAIN_AMBIENCE <= 0 ) { FOUNTAIN_AMBIENCE = TICKS_PER_SECOND * 6; playSoundEntityLocal(my, 135, 32 ); } entity = spawnGib(my); entity->flags[INVISIBLE] = false; entity->y -= 2; entity->z -= 8; entity->flags[SPRITE] = false; entity->flags[NOUPDATE] = true; entity->flags[UPDATENEEDED] = false; entity->skill[4] = 7; entity->sprite = 4; entity->yaw = (rand() % 360) * PI / 180.0; entity->pitch = (rand() % 360) * PI / 180.0; entity->roll = (rand() % 360) * PI / 180.0; entity->vel_x = 0; entity->vel_y = 0; entity->vel_z = .25; entity->fskill[3] = 0.03; } // the rest of the function is server-side. if ( multiplayer == CLIENT ) { return; } //Using the fountain (TODO: Monsters using it?). int i; for (i = 0; i < MAXPLAYERS; ++i) { if ( (i == 0 && selectedEntity == my) || (client_selected[i] == my) ) { if (inrange[i]) //Act on it only if the player (or monster, if/when this is changed to support monster interaction?) is in range. { //First check that it's not depleted. if (my->skill[0] == 0) { //Depleted messagePlayer(i, language[467]); } else { if (players[i]->entity->flags[BURNING]) { messagePlayer(i, language[468]); players[i]->entity->flags[BURNING] = false; serverUpdateEntityFlag(players[i]->entity, BURNING); steamAchievementClient(i, "BARONY_ACH_HOT_SHOWER"); } switch (my->skill[1]) { case 0: { playSoundEntity(players[i]->entity, 52, 64); //Spawn succubus. Uint32 color = SDL_MapRGB(mainsurface->format, 255, 128, 0); Entity* spawnedMonster = nullptr; if ( !strncmp(map.name, "Underworld", 10) ) { Monster creature = SUCCUBUS; if ( rand() % 2 ) { creature = INCUBUS; } for ( int c = 0; spawnedMonster == nullptr && c < 5; ++c ) { switch ( c ) { case 0: spawnedMonster = summonMonster(creature, my->x, my->y); break; case 1: spawnedMonster = summonMonster(creature, my->x + 16, my->y); break; case 2: spawnedMonster = summonMonster(creature, my->x - 16, my->y); break; case 3: spawnedMonster = summonMonster(creature, my->x, my->y + 16); break; case 4: spawnedMonster = summonMonster(creature, my->x, my->y - 16); break; } } if ( spawnedMonster ) { if ( creature == INCUBUS ) { messagePlayerColor(i, color, language[2519]); Stat* tmpStats = spawnedMonster->getStats(); if ( tmpStats ) { strcpy(tmpStats->name, "lesser incubus"); } } else { messagePlayerColor(i, color, language[469]); } } } else if ( currentlevel < 10 ) { messagePlayerColor(i, color, language[469]); spawnedMonster = summonMonster(SUCCUBUS, my->x, my->y); } else if ( currentlevel < 20 ) { if ( rand() % 2 ) { spawnedMonster = summonMonster(INCUBUS, my->x, my->y); Stat* tmpStats = spawnedMonster->getStats(); if ( tmpStats ) { strcpy(tmpStats->name, "lesser incubus"); } messagePlayerColor(i, color, language[2519]); } else { messagePlayerColor(i, color, language[469]); spawnedMonster = summonMonster(SUCCUBUS, my->x, my->y); } } else { messagePlayerColor(i, color, language[2519]); spawnedMonster = summonMonster(INCUBUS, my->x, my->y); } break; } case 1: messagePlayer(i, language[470]); messagePlayer(i, language[471]); playSoundEntity(players[i]->entity, 52, 64); stats[i]->HUNGER += 100; players[i]->entity->modHP(5); break; case 2: { //Potion effect. Potion effect is stored in my->skill[3], randomly chosen when the fountain is created. messagePlayer(i, language[470]); Item* item = newItem(static_cast<ItemType>(POTION_WATER + my->skill[3]), static_cast<Status>(4), 0, 1, 0, false, NULL); useItem(item, i); // Long live the mystical fountain of TODO. break; } case 3: { // bless all equipment playSoundEntity(players[i]->entity, 52, 64); Uint32 textcolor = SDL_MapRGB(mainsurface->format, 0, 255, 255); messagePlayerColor(i, textcolor, language[471]); messagePlayer(i, language[473]); if ( stats[i]->helmet ) { stats[i]->helmet->beatitude++; } if ( stats[i]->breastplate ) { stats[i]->breastplate->beatitude++; } if ( stats[i]->gloves ) { stats[i]->gloves->beatitude++; } if ( stats[i]->shoes ) { stats[i]->shoes->beatitude++; } if ( stats[i]->shield ) { stats[i]->shield->beatitude++; } if ( stats[i]->weapon ) { stats[i]->weapon->beatitude++; } if ( stats[i]->cloak ) { stats[i]->cloak->beatitude++; } if ( stats[i]->amulet ) { stats[i]->amulet->beatitude++; } if ( stats[i]->ring ) { stats[i]->ring->beatitude++; } if ( stats[i]->mask ) { stats[i]->mask->beatitude++; } if ( multiplayer == SERVER && i > 0 ) { strcpy((char*)net_packet->data, "BLES"); net_packet->address.host = net_clients[i - 1].host; net_packet->address.port = net_clients[i - 1].port; net_packet->len = 4; sendPacketSafe(net_sock, -1, net_packet, i - 1); } break; } case 4: { // bless one piece of equipment playSoundEntity(players[i]->entity, 52, 64); Uint32 textcolor = SDL_MapRGB(mainsurface->format, 0, 255, 255); messagePlayerColor(i, textcolor, language[471]); //Choose only one piece of equipment to bless. //First, Figure out what equipment is available. std::vector<std::pair<Item*, Uint32>> items; if ( stats[i]->helmet ) { items.push_back(std::pair<Item*,int>(stats[i]->helmet, 0)); } if ( stats[i]->breastplate ) { items.push_back(std::pair<Item*,int>(stats[i]->breastplate, 1)); } if ( stats[i]->gloves ) { items.push_back(std::pair<Item*,int>(stats[i]->gloves, 2)); } if ( stats[i]->shoes ) { items.push_back(std::pair<Item*,int>(stats[i]->shoes, 3)); } if ( stats[i]->shield ) { items.push_back(std::pair<Item*,int>(stats[i]->shield, 4)); } if ( stats[i]->weapon ) { items.push_back(std::pair<Item*,int>(stats[i]->weapon, 5)); } if ( stats[i]->cloak ) { items.push_back(std::pair<Item*,int>(stats[i]->cloak, 6)); } if ( stats[i]->amulet ) { items.push_back(std::pair<Item*,int>(stats[i]->amulet, 7)); } if ( stats[i]->ring ) { items.push_back(std::pair<Item*,int>(stats[i]->ring, 8)); } if ( stats[i]->mask ) { items.push_back(std::pair<Item*,int>(stats[i]->mask, 9)); } if ( items.size() ) { messagePlayer(i, language[2592]); //"The fountain blesses a piece of equipment" //Randomly choose a piece of equipment. std::pair<Item*, Uint32> chosen = items[rand()%items.size()]; chosen.first->beatitude++; if ( multiplayer == SERVER && i > 0 ) { strcpy((char*)net_packet->data, "BLE1"); SDLNet_Write32(chosen.second, &net_packet->data[4]); net_packet->address.host = net_clients[i - 1].host; net_packet->address.port = net_clients[i - 1].port; net_packet->len = 8; sendPacketSafe(net_sock, -1, net_packet, i - 1); } } //Does nothing if no valid items. break; } default: break; } messagePlayer(i, language[474]); my->skill[0] = 0; //Dry up fountain. serverUpdateEntitySkill(my, my->skill[0]); //TODO: messagePlayersInSight() instead. } //Then perform the effect randomly determined when the fountain was created. return; } } } }
void actFountain(Entity *my) { Entity *entity; //messagePlayer(0, "actFountain()"); //TODO: Temporary mechanism testing code. /* if( multiplayer != CLIENT ) { if (my->skill[28]) { //All it does is change its sprite to sink if it's powered. if (my->skill[28] == 1) { my->sprite = 163; } else { my->sprite = 164; } } }*/ //****************END TEST CODE*************** //TODO: Sounds. // spray water if( my->skill[0] > 0 || ( !my->skill[2] && multiplayer == CLIENT ) ) { #define FOUNTAIN_AMBIENCE my->skill[7] FOUNTAIN_AMBIENCE--; if( FOUNTAIN_AMBIENCE<=0 ) { FOUNTAIN_AMBIENCE = TICKS_PER_SECOND*6; playSoundEntityLocal(my, 135, 32 ); } entity = spawnGib(my); entity->flags[INVISIBLE] = FALSE; entity->y -= 2; entity->z -= 8; entity->flags[SPRITE] = FALSE; entity->flags[NOUPDATE] = TRUE; entity->flags[UPDATENEEDED] = FALSE; entity->skill[4] = 7; entity->sprite = 4; entity->yaw = (rand()%360)*PI/180.0; entity->pitch = (rand()%360)*PI/180.0; entity->roll = (rand()%360)*PI/180.0; entity->vel_x = 0; entity->vel_y = 0; entity->vel_z = .25; entity->fskill[3] = 0.03; } // the rest of the function is server-side. if( multiplayer==CLIENT ) return; // makes the fountain stop spraying water on clients if( my->skill[0] <= 0 ) my->skill[2] = 1; else my->skill[2] = 0; //Using the fountain (TODO: Monsters using it?). int i; for (i = 0; i < MAXPLAYERS; ++i) { if ( (i==0 && selectedEntity==my) || (client_selected[i]==my) ) { if (inrange[i]) { //Act on it only if the player (or monster, if/when this is changed to support monster interaction?) is in range. //First check that it's not depleted. if (my->skill[0] == 0) { //Depleted messagePlayer(i, language[467]); } else { if (players[i]->entity->flags[BURNING]) { messagePlayer(i, language[468]); players[i]->entity->flags[BURNING] = FALSE; if (i > 0) serverUpdateEntityFlag(players[i]->entity, BURNING); } switch (my->skill[1]) { case 0: { playSoundEntity(players[i]->entity, 52, 64); //Spawn succubus. Uint32 color = SDL_MapRGB(mainsurface->format,255,128,0); messagePlayerColor(i, color, language[469]); summonMonster(SUCCUBUS, my->x, my->y); break; } case 1: messagePlayer(i, language[470]); messagePlayer(i, language[471]); playSoundEntity(players[i]->entity, 52, 64); stats[i]->HUNGER += 50; break; case 2: { //Potion effect. Potion effect is stored in my->skill[3], randomly chosen when the fountain is created. messagePlayer(i, language[470]); Item *item = newItem(static_cast<ItemType>(POTION_WATER+my->skill[3]), static_cast<Status>(4), 0,1,0,FALSE,NULL); useItem(item,i); // Long live the mystical fountain of TODO. break; } case 3: { // bless equipment playSoundEntity(players[i]->entity, 52, 64); Uint32 textcolor = SDL_MapRGB(mainsurface->format,0,255,255); messagePlayerColor(i, textcolor, language[471]); messagePlayer(i, language[473]); if( stats[i]->helmet ) stats[i]->helmet->beatitude++; if( stats[i]->breastplate ) stats[i]->breastplate->beatitude++; if( stats[i]->gloves ) stats[i]->gloves->beatitude++; if( stats[i]->shoes ) stats[i]->shoes->beatitude++; if( stats[i]->shield ) stats[i]->shield->beatitude++; if( stats[i]->weapon ) stats[i]->weapon->beatitude++; if( stats[i]->cloak ) stats[i]->cloak->beatitude++; if( stats[i]->amulet ) stats[i]->amulet->beatitude++; if( stats[i]->ring ) stats[i]->ring->beatitude++; if( stats[i]->mask ) stats[i]->mask->beatitude++; if( multiplayer==SERVER && i>0 ) { strcpy((char *)net_packet->data,"BLES"); net_packet->address.host = net_clients[i-1].host; net_packet->address.port = net_clients[i-1].port; net_packet->len = 4; sendPacketSafe(net_sock, -1, net_packet, i-1); } break; } default: break; } messagePlayer(i, language[474]); my->skill[0] = 0; //Dry up fountain. //TODO: messagePlayersInSight() instead. } //Then perform the effect randomly determined when the fountain was created. return; } } } }
void actBoulderTrap(Entity *my) { int x, y; int c; BOULDERTRAP_AMBIENCE--; if( BOULDERTRAP_AMBIENCE<=0 ) { BOULDERTRAP_AMBIENCE = TICKS_PER_SECOND*30; playSoundEntity( my, 149, 64 ); } if( !my->skill[28] ) return; // received on signal if( my->skill[28] == 2) { if( !BOULDERTRAP_FIRED ) { playSoundEntity(my, 150, 128); for( c=0; c<MAXPLAYERS; c++ ) playSoundPlayer(c, 150, 64); BOULDERTRAP_FIRED = 1; for( c=0; c<4; c++ ) { switch( c ) { case 0: x = 16; y = 0; break; case 1: x = 0; y = 16; break; case 2: x = -16; y = 0; break; case 3: x = 0; y = -16; break; } x = ((int)(x+my->x))>>4; y = ((int)(y+my->y))>>4; if( x>=0 && y>=0 && x<map.width && y<map.height ) { if( !map.tiles[OBSTACLELAYER+y*MAPLAYERS+x*MAPLAYERS*map.height] ) { Entity *entity = newEntity(245, 1, map.entities); // boulder entity->parent = my->uid; entity->x = (x<<4)+8; entity->y = (y<<4)+8; entity->z = -64; entity->yaw = c*(PI/2.f); entity->sizex = 7; entity->sizey = 7; if( checkObstacle( entity->x+cos(entity->yaw)*16, entity->y+sin(entity->yaw)*16, entity, NULL ) ) { entity->yaw += PI*(rand()%2)-PI/2; if( entity->yaw >= PI*2 ) entity->yaw -= PI*2; else if( entity->yaw < 0 ) entity->yaw += PI*2; } entity->behavior = &actBoulder; entity->flags[UPDATENEEDED] = TRUE; entity->flags[PASSABLE] = TRUE; } } } } }
void actMinotaurCeilingBuster(Entity* my) { double x, y; // levitate particles int u = std::min<unsigned int>(std::max<int>(0, my->x / 16), map.width - 1); int v = std::min<unsigned int>(std::max<int>(0, my->y / 16), map.height - 1); if ( !map.tiles[v * MAPLAYERS + u * MAPLAYERS * map.height] ) { int c; for ( c = 0; c < 2; c++ ) { Entity* entity = newEntity(171, 1, map.entities, nullptr); //Particle entity. entity->x = my->x - 8 + rand() % 17; entity->y = my->y - 8 + rand() % 17; entity->z = 10 + rand() % 3; entity->scalex = 0.7; entity->scaley = 0.7; entity->scalez = 0.7; entity->sizex = 1; entity->sizey = 1; entity->yaw = (rand() % 360) * PI / 180.f; entity->flags[PASSABLE] = true; entity->flags[BRIGHT] = true; entity->flags[NOUPDATE] = true; entity->flags[UNCLICKABLE] = true; entity->behavior = &actMagicParticle; if ( multiplayer != CLIENT ) { entity_uids--; } entity->setUID(-3); } } // bust ceilings for ( x = my->x - my->sizex - 1; x <= my->x + my->sizex + 1; x += 1 ) { for ( y = my->y - my->sizey - 1; y <= my->y + my->sizey + 1; y += 1 ) { if ( x >= 0 && y >= 0 && x < map.width << 4 && y < map.height << 4 ) { int index = (MAPLAYERS - 1) + ((int)floor(y / 16)) * MAPLAYERS + ((int)floor(x / 16)) * MAPLAYERS * map.height; if ( map.tiles[index] ) { if ( my->monsterAttack == 0 ) { if ( multiplayer != CLIENT ) { my->attack(MONSTER_POSE_MELEE_WINDUP2, 0, nullptr); } return; } else if ( my->monsterAttack == MONSTER_POSE_MELEE_WINDUP2 ) { return; } map.tiles[index] = 0; if ( multiplayer != CLIENT ) { playSoundEntity(my, 67, 128); //MONSTER_ATTACK = 1; Stat* myStats = my->getStats(); if ( myStats ) { // easy hack to stop the minotaur while he breaks stuff myStats->EFFECTS[EFF_PARALYZED] = true; myStats->EFFECTS_TIMERS[EFF_PARALYZED] = 10; } } // spawn several rock particles (NOT items) int c, i = 6 + rand() % 4; for ( c = 0; c < i; c++ ) { Entity *entity = nullptr; if ( multiplayer == SERVER ) { entity = spawnGib(my); } else { entity = spawnGibClient(my->x, my->y, my->z, 5); } if ( entity ) { entity->x = ((int)(my->x / 16)) * 16 + rand() % 16; entity->y = ((int)(my->y / 16)) * 16 + rand() % 16; entity->z = -8; entity->flags[PASSABLE] = true; entity->flags[INVISIBLE] = false; entity->flags[NOUPDATE] = true; entity->flags[UPDATENEEDED] = false; entity->sprite = items[GEM_ROCK].index; entity->yaw = rand() % 360 * PI / 180; entity->pitch = rand() % 360 * PI / 180; entity->roll = rand() % 360 * PI / 180; entity->vel_x = (rand() % 20 - 10) / 10.0; entity->vel_y = (rand() % 20 - 10) / 10.0; entity->vel_z = -.25; entity->fskill[3] = 0.03; } } } node_t* node, *nextnode; std::vector<list_t*> entLists = TileEntityList.getEntitiesWithinRadiusAroundEntity(my, 2); for ( std::vector<list_t*>::iterator it = entLists.begin(); it != entLists.end(); ++it ) { list_t* currentList = *it; for ( node = currentList->first; node != nullptr; node = nextnode ) { nextnode = node->next; Entity* entity = (Entity*)node->element; if ( (int)(x / 16) == (int)(entity->x / 16) && (int)(y / 16) == (int)(entity->y / 16) ) { if ( entity->behavior == &actDoorFrame ) { // spawn several rock items int c, i = 8 + rand() % 4; for ( c = 0; c < i; c++ ) { Entity *entity = nullptr; if ( multiplayer == SERVER ) { entity = spawnGib(my); } else { entity = spawnGibClient(my->x, my->y, my->z, 5); } if ( entity ) { entity->x = ((int)(my->x / 16)) * 16 + rand() % 16; entity->y = ((int)(my->y / 16)) * 16 + rand() % 16; entity->z = -8; entity->flags[PASSABLE] = true; entity->flags[INVISIBLE] = false; entity->flags[NOUPDATE] = true; entity->flags[UPDATENEEDED] = false; entity->sprite = items[GEM_ROCK].index; entity->yaw = rand() % 360 * PI / 180; entity->pitch = rand() % 360 * PI / 180; entity->roll = rand() % 360 * PI / 180; entity->vel_x = (rand() % 20 - 10) / 10.0; entity->vel_y = (rand() % 20 - 10) / 10.0; entity->vel_z = -.25; entity->fskill[3] = 0.03; } } list_RemoveNode(entity->mynode); } else if ( entity->behavior == &actDoor ) { if ( multiplayer != CLIENT ) { entity->skill[4] = 0; // destroy the door } } else if ( entity->behavior == &actGate ) { if ( multiplayer != CLIENT ) { playSoundEntity(entity, 76, 64); list_RemoveNode(entity->mynode); } } else if ( entity->behavior == &actStalagCeiling || entity->behavior == &actStalagFloor || entity->behavior == &actStalagColumn ) { // spawn several rock items int c, i = rand() % 4; for ( c = 0; c < i; ++c ) { //Entity* childEntity = spawnGib(my); Entity *childEntity = nullptr; if ( multiplayer == SERVER ) { childEntity = spawnGib(my); } else { childEntity = spawnGibClient(my->x, my->y, my->z, 5); } if ( entity ) { childEntity->x = ((int)(my->x / 16)) * 16 + rand() % 16; childEntity->y = ((int)(my->y / 16)) * 16 + rand() % 16; childEntity->z = -8; childEntity->flags[PASSABLE] = true; childEntity->flags[INVISIBLE] = false; childEntity->flags[NOUPDATE] = true; childEntity->flags[UPDATENEEDED] = false; childEntity->sprite = items[GEM_ROCK].index; childEntity->yaw = rand() % 360 * PI / 180; childEntity->pitch = rand() % 360 * PI / 180; childEntity->roll = rand() % 360 * PI / 180; childEntity->vel_x = (rand() % 20 - 10) / 10.0; childEntity->vel_y = (rand() % 20 - 10) / 10.0; childEntity->vel_z = -.25; childEntity->fskill[3] = 0.03; } } list_RemoveNode(entity->mynode); } } } } } } } }
int boulderCheckAgainstEntity(Entity *my, Entity *entity) { if (!my || !entity) return 0; if( entity->behavior == &actPlayer || entity->behavior == &actMonster ) { if( entityInsideEntity( my, entity ) ) { Stat *stats = entity->getStats(); if( stats ) { if( entity->behavior==&actPlayer ) { Uint32 color = SDL_MapRGB(mainsurface->format,255,0,0); messagePlayerColor(entity->skill[2],color,language[455]); if( entity->skill[2] == clientnum ) { camera_shakex += .1; camera_shakey += 10; } else { strcpy((char *)net_packet->data,"SHAK"); net_packet->data[4]=10; // turns into .1 net_packet->data[5]=10; net_packet->address.host = net_clients[entity->skill[2]-1].host; net_packet->address.port = net_clients[entity->skill[2]-1].port; net_packet->len = 6; sendPacketSafe(net_sock, -1, net_packet, entity->skill[2]-1); } } playSoundEntity(my,181,128); playSoundEntity(entity,28,64); spawnGib(entity); entity->modHP(-80); entity->setObituary(language[1505]); if( entity->behavior==&actPlayer ) if( stats->HP<=0 ) steamAchievementClient(entity->skill[2],"BARONY_ACH_THROW_ME_THE_WHIP"); if( stats->HP > 0 ) { // spawn several rock items int i = 8+rand()%4; int c; for( c=0; c<i; c++ ) { Entity *entity = newEntity(-1,1,map.entities); entity->flags[INVISIBLE]=TRUE; entity->flags[UPDATENEEDED]=TRUE; entity->x = my->x - 4 + rand()%8; entity->y = my->y - 4 + rand()%8; entity->z = -6+rand()%12; entity->sizex = 4; entity->sizey = 4; entity->yaw = rand()%360 * PI/180; entity->vel_x = (rand()%20-10)/10.0; entity->vel_y = (rand()%20-10)/10.0; entity->vel_z = -.25 - (rand()%5)/10.0; entity->flags[PASSABLE] = TRUE; entity->behavior = &actItem; entity->flags[USERFLAG1] = TRUE; // no collision: helps performance entity->skill[10] = GEM_ROCK; // type entity->skill[11] = WORN; // status entity->skill[12] = 0; // beatitude entity->skill[13] = 1; // count entity->skill[14] = 0; // appearance entity->skill[15] = FALSE; // identified } double ox = my->x; double oy = my->y; // destroy the boulder playSoundEntity(my,67,128); list_RemoveNode(my->mynode); // on sokoban, destroying boulders spawns scorpions if( !strcmp(map.name,"Sokoban") ) { Entity *monster = summonMonster(SCORPION,ox,oy); if( monster ) { int c; for( c=0; c<MAXPLAYERS; c++ ) { Uint32 color = SDL_MapRGB(mainsurface->format,255,128,0); messagePlayerColor(c,color,language[406]); } } } return 1; } } } } else if( entity->behavior == &actGate || entity->behavior == &actBoulder || entity->behavior==&actChest || entity->behavior==&actHeadstone || entity->behavior==&actFountain || entity->behavior==&actSink ) { if( !entity->flags[PASSABLE] ) { if( entityInsideEntity( my, entity ) ) { // stop the boulder BOULDER_STOPPED=1; BOULDER_ROLLING=0; playSoundEntity(my,181,128); if( my->flags[PASSABLE] ) { my->flags[PASSABLE] = FALSE; if( multiplayer==SERVER ) serverUpdateEntityFlag(my,PASSABLE); } } } } else if( entity->behavior == &actDoor ) { if( entityInsideEntity( my, entity ) ) { playSoundEntity(entity,28,64); entity->skill[4] = 0; if( !entity->skill[0] ) entity->skill[6] = (my->x > entity->x); else entity->skill[6] = (my->y < entity->y); playSoundEntity(my,181,128); } } return 0; }
void actGoldBag(Entity *my) { int i; if( my->flags[INVISIBLE] ) { if( multiplayer!=CLIENT ) { node_t *node; for( node=map.entities->first; node!=NULL; node=node->next ) { Entity *entity = (Entity *)node->element; if( entity->sprite == 245 ) // boulder.vox return; } my->flags[INVISIBLE] = FALSE; serverUpdateEntityFlag(my,INVISIBLE); if( !strcmp(map.name,"Sokoban") ) { for( i=0; i<MAXPLAYERS; i++ ) steamAchievementClient(i,"BARONY_ACH_PUZZLE_MASTER"); } } else { return; } } GOLDBAG_AMBIENCE--; if( GOLDBAG_AMBIENCE<=0 ) { GOLDBAG_AMBIENCE = TICKS_PER_SECOND*30; playSoundEntityLocal( my, 149, 16 ); } // pick up gold if( multiplayer!=CLIENT ) { for(i=0;i<MAXPLAYERS;i++) { if( (i==0 && selectedEntity==my) || (client_selected[i]==my) ) { if(inrange[i]) { if (players[i] && players[i]->entity) playSoundEntity(players[i]->entity, 242+rand()%4, 64 ); stats[i]->GOLD += GOLDBAG_AMOUNT; if( i!=0 ) { if( multiplayer==SERVER ) { // send the client info on the gold it picked up strcpy((char *)net_packet->data,"GOLD"); SDLNet_Write32(stats[i]->GOLD,&net_packet->data[4]); net_packet->address.host = net_clients[i-1].host; net_packet->address.port = net_clients[i-1].port; net_packet->len = 8; sendPacketSafe(net_sock, -1, net_packet, i-1); } } // message for item pickup if( GOLDBAG_AMOUNT==1 ) messagePlayer(i,language[483]); else messagePlayer(i,language[484],GOLDBAG_AMOUNT); // remove gold entity list_RemoveNode(my->mynode); return; } } } } else { my->flags[NOUPDATE] = TRUE; } }
void actSwitch(Entity* my) { //TODO: If powered on, and it detects a depowered neighbor, it should pulse that neighbor to turn on. //Thus, this function needs to be called periodically. //This is so that if a switch goes off and there's another switch on the network, the entire network shuts off regardless of the other switch's status. //So then when that second switch's actSwitch() comes up, and if it's on, it'll repower the entire network -- which will stay powered until ALL connected switches go off. my->flags[PASSABLE] = true; // these should ALWAYS be passable. No exceptions if ( multiplayer != CLIENT ) { int i = 0; for (i = 0; i < MAXPLAYERS; ++i) { if ( (i == 0 && selectedEntity == my) || (client_selected[i] == my) ) { if (inrange[i]) //Act on it only if the player (or monster, if/when this is changed to support monster interaction?) is in range. { messagePlayer(i, language[1110]); playSoundEntity(my, 56, 64); my->toggleSwitch(); } } } if ( my->isInteractWithMonster() ) { my->toggleSwitch(); my->clearMonsterInteract(); } if (my->skill[0]) { //Power on any neighbors that don't have power. my->switchUpdateNeighbors(); //TODO: Alternatively, instead of using CPU cycles on this, have the recursive network shutdown alert any switches connected to it that are powered on that it's shutting down, so that they can repower the network come next frame. } } else { my->flags[NOUPDATE] = true; } // Rotate the switch when it is on/off. if ( my->skill[0] ) { if ( my->roll > -PI / 4 ) { my->roll -= std::max<real_t>((my->roll + PI / 4) / 2, .05); } else { my->roll = -PI / 4; } } else { if ( my->roll < PI / 4 ) { my->roll += std::max<real_t>(-(my->roll - PI / 4) / 2, .05); } else { my->roll = PI / 4; } } }
Entity* castSpell(Uint32 caster_uid, spell_t *spell, bool using_magicstaff, bool trap) { Entity *caster = uidToEntity(caster_uid); if (!caster || !spell) { //Need a spell and caster to cast a spell. return NULL; } Entity *result = NULL; //If the spell spawns an entity (like a magic light ball or a magic missile), it gets stored here and returned. #define spellcasting std::min(std::max(0,stat->PROFICIENCIES[PRO_SPELLCASTING]+statGetINT(stat)),100) //Shortcut! if (clientnum != 0 && multiplayer == CLIENT) { strcpy( (char *)net_packet->data, "SPEL" ); net_packet->data[4] = clientnum; SDLNet_Write32(spell->ID, &net_packet->data[5]); net_packet->address.host = net_server.host; net_packet->address.port = net_server.port; net_packet->len = 9; sendPacketSafe(net_sock, -1, net_packet, 0); return NULL; } if (!spell->elements.first) { return NULL; } //node_t *node = spell->types->first; #define PROPULSION_MISSILE 1 int i = 0; int chance = 0; int propulsion = 0; int traveltime = 0; int magiccost = 0; int extramagic = 0; //Extra magic drawn in from the caster being a newbie. int extramagic_to_use = 0; //Instead of doing element->mana (which causes bugs), this is an extra factor in the mana equations. Pumps extra mana into elements from extramagic. Entity *entity = NULL; spell_t *channeled_spell=NULL; //Pointer to the spell if it's a channeled spell. For the purpose of giving it its node in the channeled spell list. node_t *node = spell->elements.first; stat_t *stat = caster->getStats(); int player = -1; for (i = 0; i < numplayers; ++i) { if (caster == players[i]) { player = i; //Set the player. } } bool newbie = FALSE; if( !using_magicstaff && !trap) { if (stat->PROFICIENCIES[PRO_SPELLCASTING] < SPELLCASTING_BEGINNER) { newbie = TRUE; //The caster has lower spellcasting skill. Cue happy fun times. } /*magiccost = getCostOfSpell(spell); if (magiccost < 0) { if (player >= 0) messagePlayer(player, "Error: Invalid spell. Mana cost is negative?"); return NULL; }*/ if (multiplayer == SINGLE) { magiccost = cast_animation.mana_left; caster->drainMP(magiccost); } else { magiccost = getCostOfSpell(spell); caster->drainMP(magiccost); } } if (newbie) { //So This wizard is a newbie. //First, drain some extra mana maybe. int chance = rand()%10; if (chance >= spellcasting/10) { //At skill 20, there's an 80% chance you'll use extra mana. At 70, there's a 30% chance. extramagic = rand()%(300/(spellcasting+1)); //Use up extra mana. More mana used the lower your spellcasting skill. extramagic = std::min(extramagic, stat->MP / 10); //To make sure it doesn't draw, say, 5000 mana. Cause dammit, if you roll a 1 here...you're doomed. caster->drainMP(extramagic); } //Now, there's a chance they'll fumble the spell. chance = rand()%10; if (chance >= spellcasting/10) { if (rand()%3 == 1) { //Fizzle the spell. //TODO: Cool effects. playSoundEntity(caster,163,128); if (player >= 0) messagePlayer(player, language[409]); return NULL; } } } //Check if the bugger is levitating. bool levitating = FALSE; if (!trap) { if (stat->EFFECTS[EFF_LEVITATING] == TRUE ) levitating=TRUE; if (stat->ring != NULL ) if (stat->ring->type == RING_LEVITATION ) levitating = TRUE; if (stat->shoes != NULL) if (stat->shoes->type == STEEL_BOOTS_LEVITATION ) levitating = TRUE; } //Water walking boots bool waterwalkingboots = FALSE; if (!trap) { if (stat->shoes != NULL) if (stat->shoes->type == IRON_BOOTS_WATERWALKING ) waterwalkingboots = TRUE; } node_t *node2; //For traversing the map looking for...liquids? //Check if swimming. if (!waterwalkingboots && !levitating && !trap && player>=0) { bool swimming=FALSE; if( players[player] ) { int x = std::min<int>(std::max(0.0,floor(caster->x/16)),map.width-1); int y = std::min<int>(std::max(0.0,floor(caster->y/16)),map.height-1); if( animatedtiles[map.tiles[y*MAPLAYERS+x*MAPLAYERS*map.height]] ) swimming=TRUE; } if( swimming ) { //Can't cast spells while swimming if not levitating or water walking. if (player >= 0) messagePlayer(player, language[410]); return NULL; } } //Right. First, grab the root element, which is what determines the delivery system. //spellElement_t *element = (spellElement_t *)spell->elements->first->element; spellElement_t *element = (spellElement_t *)node->element; if (element) { extramagic_to_use = 0; /*if (magiccost > stat->MP) { if (player >= 0) messagePlayer(player, "Insufficient mana!"); //TODO: Allow overexpending at the cost of extreme danger? (maybe an immensely powerful tree of magic actually likes this -- using your life-force to power spells instead of mana) return NULL; }*/ if (extramagic > 0) { //Extra magic. Pump it in here? chance = rand()%5; if (chance == 1) { //Use some of that extra magic in this element. int amount = rand()%extramagic; extramagic -= amount; extramagic_to_use += amount; } } if (!strcmp(element->name, spellElement_missile.name)) { //Set the propulsion to missile. propulsion = PROPULSION_MISSILE; traveltime = element->duration; if (newbie) { //This guy's a newbie. There's a chance they've screwed up and negatively impacted the efficiency of the spell. chance = rand()%10; if (chance >= spellcasting/10) traveltime -= rand()%(1000/(spellcasting+1)); if (traveltime < 30) traveltime = 30; //Range checking. } traveltime += (((element->mana + extramagic_to_use) - element->base_mana) / element->overload_multiplier) * element->duration; } else if (!strcmp(element->name, spellElement_light.name)) { entity = newEntity(175, 1, map.entities); // black magic ball entity->parent = caster->uid; entity->x = caster->x; entity->y = caster->y; entity->z = -5.5 + ((-6.5f + -4.5f) / 2) * sin(0); entity->skill[7] = -5.5; //Base z. entity->sizex = 1; entity->sizey = 1; entity->yaw = caster->yaw; entity->flags[UPDATENEEDED]=TRUE; entity->flags[PASSABLE]=TRUE; entity->flags[BRIGHT]=TRUE; entity->behavior=&actMagiclightBall; entity->skill[4] = entity->x; //Store what x it started shooting out from the player at. entity->skill[5] = entity->y; //Store what y it started shooting out from the player at. entity->skill[12] = (element->duration * (((element->mana + extramagic_to_use) / element->base_mana) * element->overload_multiplier)); //How long this thing lives. node_t *spellnode = list_AddNodeLast(&entity->children); spellnode->element = copySpell(spell); //We need to save the spell since this is a channeled spell. channeled_spell = (spell_t*)(spellnode->element); spellnode->size = sizeof(spell_t); ((spell_t *)spellnode->element)->caster = caster->uid; if( using_magicstaff ) ((spell_t *)spellnode->element)->magicstaff = TRUE; spellnode->deconstructor = &spellDeconstructor; if (newbie) { //This guy's a newbie. There's a chance they've screwed up and negatively impacted the efficiency of the spell. chance = rand()%10; if (chance >= spellcasting/10) { // lifespan of the lightball entity->skill[12] -= rand()%(2000/(spellcasting+1)); if (entity->skill[12] < 180) entity->skill[12] = 180; //Range checking. } } if (using_magicstaff || trap) { entity->skill[12] = MAGICSTAFF_LIGHT_DURATION; //TODO: Grab the duration from the magicstaff or trap? ((spell_t *)spellnode->element)->sustain = FALSE; } else { entity->skill[12] /= getCostOfSpell((spell_t *)spellnode->element); } ((spell_t *)spellnode->element)->channel_duration = entity->skill[12]; //Tell the spell how long it's supposed to last so that it knows what to reset its timer to. result = entity; playSoundEntity(entity, 165, 128 ); } else if (!strcmp(element->name, spellElement_invisible.name)) { int duration = element->duration; duration += (((element->mana + extramagic_to_use) - element->base_mana) / element->overload_multiplier) * element->duration; node_t *spellnode = list_AddNodeLast(&caster->getStats()->magic_effects); spellnode->element = copySpell(spell); //We need to save the spell since this is a channeled spell. channeled_spell = (spell_t*)(spellnode->element); channeled_spell->magic_effects_node = spellnode; spellnode->size = sizeof(spell_t); ((spell_t *)spellnode->element)->caster = caster->uid; spellnode->deconstructor = &spellDeconstructor; if (newbie) { //This guy's a newbie. There's a chance they've screwed up and negatively impacted the efficiency of the spell. chance = rand()%10; if (chance >= spellcasting/10) duration -= rand()%(1000/(spellcasting+1)); if (duration < 180) duration = 180; //Range checking. } duration /= getCostOfSpell((spell_t *)spellnode->element); channeled_spell->channel_duration = duration; //Tell the spell how long it's supposed to last so that it knows what to reset its timer to. stat->EFFECTS[EFF_INVISIBLE] = TRUE; stat->EFFECTS_TIMERS[EFF_INVISIBLE] = duration; for (i = 0; i < numplayers; ++i) { if (caster == players[i]) { serverUpdateEffects(i); } } playSoundEntity(caster, 166, 128 ); spawnMagicEffectParticles(caster->x,caster->y,caster->z,174); } else if (!strcmp(element->name, spellElement_levitation.name)) { int duration = element->duration; duration += (((element->mana + extramagic_to_use) - element->base_mana) / element->overload_multiplier) * element->duration; node_t *spellnode = list_AddNodeLast(&caster->getStats()->magic_effects); spellnode->element = copySpell(spell); //We need to save the spell since this is a channeled spell. channeled_spell = (spell_t*)(spellnode->element); channeled_spell->magic_effects_node = spellnode; spellnode->size = sizeof(spell_t); ((spell_t *)spellnode->element)->caster = caster->uid; spellnode->deconstructor = &spellDeconstructor; if (newbie) { //This guy's a newbie. There's a chance they've screwed up and negatively impacted the efficiency of the spell. chance = rand()%10; if (chance >= spellcasting/10) duration -= rand()%(1000/(spellcasting+1)); if (duration < 180) duration = 180; //Range checking. } duration /= getCostOfSpell((spell_t *)spellnode->element); channeled_spell->channel_duration = duration; //Tell the spell how long it's supposed to last so that it knows what to reset its timer to. stat->EFFECTS[EFF_LEVITATING] = TRUE; stat->EFFECTS_TIMERS[EFF_LEVITATING] = duration; for (i = 0; i < numplayers; ++i) { if (caster == players[i]) { serverUpdateEffects(i); } } playSoundEntity(caster, 178, 128 ); spawnMagicEffectParticles(caster->x,caster->y,caster->z,170); } else if (!strcmp(element->name, spellElement_teleportation.name)) { caster->teleportRandom(); } else if (!strcmp(element->name, spellElement_identify.name)) { for (i = 0; i < numplayers; ++i) { if (caster == players[i]) { spawnMagicEffectParticles(caster->x,caster->y,caster->z,171); if (i != 0) { //Tell the client to identify an item. strcpy((char *)net_packet->data,"IDEN"); net_packet->address.host = net_clients[i - 1].host; net_packet->address.port = net_clients[i - 1].port; net_packet->len = 4; sendPacketSafe(net_sock, -1, net_packet, i-1); } else { //Identify an item. shootmode = FALSE; gui_mode = GUI_MODE_INVENTORY; //Reset the GUI to the inventory. identifygui_active = TRUE; identifygui_appraising = FALSE; //identifygui_mode = TRUE; } } } playSoundEntity(caster, 167, 128 ); } else if (!strcmp(element->name, spellElement_removecurse.name)) { for (i = 0; i < numplayers; ++i) { if (caster == players[i]) { spawnMagicEffectParticles(caster->x,caster->y,caster->z,169); if (i != 0) { //Tell the client to uncurse an item. strcpy((char *)net_packet->data,"RCUR"); net_packet->address.host = net_clients[i - 1].host; net_packet->address.port = net_clients[i - 1].port; net_packet->len = 4; sendPacketSafe(net_sock, -1, net_packet, i-1); } else { //Uncurse an item shootmode = FALSE; gui_mode = GUI_MODE_INVENTORY; //Reset the GUI to the inventory. removecursegui_active = TRUE; } } } playSoundEntity(caster, 167, 128 ); } else if (!strcmp(element->name, spellElement_magicmapping.name)) { for (i = 0; i < numplayers; ++i) { if (caster == players[i]) { spawnMagicEffectParticles(caster->x,caster->y,caster->z,171); spell_magicMap(i); } } playSoundEntity(caster, 167, 128 ); } else if (!strcmp(element->name, spellElement_heal.name)) { //TODO: Make it work for NPCs. for (i = 0; i < numplayers; ++i) { if (caster == players[i]) { int amount = element->damage * (((element->mana + extramagic_to_use) / element->base_mana) * element->overload_multiplier); //Amount to heal. if (newbie) { //This guy's a newbie. There's a chance they've screwed up and negatively impacted the efficiency of the spell. chance = rand()%10; if (chance >= spellcasting/10) amount -= rand()%(1000/(spellcasting+1)); if (amount < 8) amount = 8; //Range checking. } spell_changeHealth(players[i], amount); playSoundEntity(caster, 168, 128); for(node = map.entities->first; node->next; node = node->next) { entity = (Entity *)(node->element); if ( !entity || entity==caster ) continue; if( entity->behavior!=&actPlayer && entity->behavior!=&actMonster ) continue; if (entityDist(entity, caster) <= HEAL_RADIUS && entity->checkFriend(caster)) { spell_changeHealth(entity, amount); playSoundEntity(entity, 168, 128); spawnMagicEffectParticles(entity->x,entity->y,entity->z,169); } } break; } } playSoundEntity(caster, 168, 128); spawnMagicEffectParticles(caster->x,caster->y,caster->z,169); } else if (!strcmp(element->name, spellElement_cure_ailment.name)) { //TODO: Generalize it for NPCs too? for (i = 0; i < numplayers; ++i) { if (caster == players[i]) { Uint32 color = SDL_MapRGB(mainsurface->format,0,255,0); messagePlayerColor(i,color,language[411]); int c = 0; for (c = 0; c < NUMEFFECTS; ++c) { //This does a whole lot more than just cure ailments. stats[i].EFFECTS[c]=FALSE; stats[i].EFFECTS_TIMERS[c]=0; } serverUpdateEffects(player); playSoundEntity(entity, 168, 128); for(node = map.entities->first; node->next; node = node->next) { entity = (Entity *)(node->element); if( !entity || entity==caster ) continue; if( entity->behavior!=&actPlayer && entity->behavior!=&actMonster ) continue; stat_t *target_stat = entity->getStats(); if( target_stat ) { if (entityDist(entity, caster) <= HEAL_RADIUS && entity->checkFriend(caster)) { for (c = 0; c < NUMEFFECTS; ++c) { //This does a whole lot more than just cure ailments. target_stat->EFFECTS[c]=FALSE; target_stat->EFFECTS_TIMERS[c]=0; } if( entity->behavior==&actPlayer ) serverUpdateEffects(entity->skill[2]); if( entity->flags[BURNING] ) { entity->flags[BURNING] = FALSE; serverUpdateEntityFlag(entity,BURNING); } playSoundEntity(entity, 168, 128); spawnMagicEffectParticles(entity->x,entity->y,entity->z,169); } } } break; } } playSoundEntity(caster, 168, 128 ); spawnMagicEffectParticles(caster->x,caster->y,caster->z,169); } if (propulsion == PROPULSION_MISSILE) { entity = newEntity(168, 1, map.entities); // red magic ball entity->parent = caster->uid; entity->x = caster->x; entity->y = caster->y; entity->z = -1; entity->sizex = 1; entity->sizey = 1; entity->yaw = caster->yaw; entity->flags[UPDATENEEDED]=TRUE; entity->flags[PASSABLE]=TRUE; entity->flags[BRIGHT]=TRUE; entity->behavior = &actMagicMissile; double missile_speed = 4 * ((double)element->mana / element->overload_multiplier); //TODO: Factor in base mana cost? entity->vel_x = cos(entity->yaw) * (missile_speed); entity->vel_y = sin(entity->yaw) * (missile_speed); entity->skill[4] = 0; entity->skill[5] = traveltime; node = list_AddNodeFirst(&entity->children); node->element = copySpell(spell); ((spell_t *)node->element)->caster = caster->uid; node->deconstructor = &spellDeconstructor; node->size = sizeof(spell_t); if( !strcmp(spell->name, spell_fireball.name) ) playSoundEntity(entity, 164, 128 ); else if( !strcmp(spell->name, spell_lightning.name) ) playSoundEntity(entity, 171, 128 ); else if( !strcmp(spell->name, spell_cold.name) ) playSoundEntity(entity, 172, 128 ); else playSoundEntity(entity, 169, 128 ); result = entity; } extramagic_to_use = 0; if (extramagic > 0) { //Extra magic. Pump it in here? chance = rand()%5; if (chance == 1) { //Use some of that extra magic in this element. int amount = rand()%extramagic; extramagic -= amount; extramagic_to_use += amount; //TODO: Make the elements here use this? Looks like they won't, currently. Oh well. } } //TODO: Add the status/conditional elements/modifiers (probably best as elements) too. Like onCollision or something. //element = (spellElement_t *)element->elements->first->element; node = element->elements.first; if( node ) { element = (spellElement_t *)node->element; if (!strcmp(element->name, spellElement_force.name)) { //Give the spell force properties. if (propulsion == PROPULSION_MISSILE) { entity->sprite = 173; } if (newbie) { //This guy's a newbie. There's a chance they've screwed up and negatively impacted the efficiency of the spell. chance = rand()%10; if (chance >= spellcasting/10) element->damage -= rand()%(100/(spellcasting+1)); if (element->damage < 10) element->damage = 10; //Range checking. } } else if (!strcmp(element->name, spellElement_fire.name)) { if (propulsion == PROPULSION_MISSILE) { entity->sprite = 168; //entity->skill[4] = entity->x; //Store what x it started shooting out from the player at. //entity->skill[5] = entity->y; //Store what y it started shooting out from the player at. //entity->skill[12] = (100 * stat->PROFICIENCIES[PRO_SPELLCASTING]) + (100 * stat->PROFICIENCIES[PRO_MAGIC]) + (100 * (rand()%10)) + (10 * (rand()%10)) + (rand()%10); //How long this thing lives. //playSoundEntity( entity, 59, 128 ); } if (newbie) { //This guy's a newbie. There's a chance they've screwed up and negatively impacted the efficiency of the spell. chance = rand()%10; if (chance >= spellcasting/10) element->damage -= rand()%(100/(spellcasting+1)); if (element->damage < 10) element->damage = 10; //Range checking. } } else if (!strcmp(element->name, spellElement_lightning.name)) { if (propulsion == PROPULSION_MISSILE) { entity->sprite = 170; } if (newbie) { //This guy's a newbie. There's a chance they've screwed up and negatively impacted the efficiency of the spell. chance = rand()%10; if (chance >= spellcasting/10) element->damage -= rand()%(100/(spellcasting+1)); if (element->damage < 10) element->damage = 10; //Range checking. } } else if (!strcmp(element->name, spellElement_confuse.name)) { if (propulsion == PROPULSION_MISSILE) { entity->sprite = 173; } } else if (!strcmp(element->name, spellElement_cold.name)) { if (propulsion == PROPULSION_MISSILE) { entity->sprite = 172; } } else if (!strcmp(element->name, spellElement_dig.name)) { if (propulsion == PROPULSION_MISSILE) { entity->sprite = 171; } } else if (!strcmp(element->name, spellElement_locking.name)) { if (propulsion == PROPULSION_MISSILE) { entity->sprite = 171; } } else if (!strcmp(element->name, spellElement_opening.name)) { if (propulsion == PROPULSION_MISSILE) { entity->sprite = 171; } } else if (!strcmp(element->name, spellElement_slow.name)) { if (propulsion == PROPULSION_MISSILE) { entity->sprite = 171; } } else if (!strcmp(element->name, spellElement_sleep.name)) { if (propulsion == PROPULSION_MISSILE) { entity->sprite = 172; } } else if (!strcmp(spell->name, spell_magicmissile.name)) { if (propulsion == PROPULSION_MISSILE) { entity->sprite = 173; } } } } //Random chance to level up spellcasting skill. if(rand()%4==0) { caster->increaseSkill(PRO_SPELLCASTING); } if(rand()%5==0) { caster->increaseSkill(PRO_MAGIC); // otherwise you will basically never be able to learn all the spells in the game... } if (spell_isChanneled(spell) && !using_magicstaff) { //TODO: What about magic traps and channeled spells? if (!channeled_spell) { printlog( "What. Spell is channeled but no channeled_spell pointer? What sorcery is this?\n"); } else { int target_client = 0; for (i = 0; i < numplayers; ++i) { if (players[i] == caster) { target_client = i; } } //printlog( "Client is: %d\n", target_client); if (multiplayer == SERVER && target_client != 0) { strcpy( (char *)net_packet->data, "CHAN" ); net_packet->data[4] = clientnum; SDLNet_Write32(spell->ID, &net_packet->data[5]); net_packet->address.host = net_clients[target_client - 1].host; net_packet->address.port = net_clients[target_client - 1].port; net_packet->len = 9; sendPacketSafe(net_sock, -1, net_packet, target_client-1); } //Add this spell to the list of channeled spells. node = list_AddNodeLast(&channeledSpells[target_client]); node->element = channeled_spell; node->size = sizeof(spell_t); node->deconstructor = &emptyDeconstructor; channeled_spell->sustain_node = node; } } return result; }
void actSwitchWithTimer(Entity* my) { my->flags[PASSABLE] = true; // these should ALWAYS be passable. No exceptions if ( multiplayer != CLIENT ) { int i = 0; for ( i = 0; i < MAXPLAYERS; ++i ) { if ( (i == 0 && selectedEntity == my) || (client_selected[i] == my) ) { // server/client has clicked on the entity. if ( inrange[i] ) //Act on it only if the player (or monster, if/when this is changed to support monster interaction?) is in range. { switch ( my->leverStatus ) { case 0: messagePlayer(i, language[2360]); break; case 1: messagePlayer(i, language[2361]); break; case 2: messagePlayer(i, language[2362]); break; default: messagePlayer(i, language[2363]); break; } if ( my->leverStatus < 3 ) { ++my->leverStatus; playSoundEntity(my, 248, 64); serverUpdateEntitySkill(my, 1); if ( my->leverStatus == 3 ) { playSoundEntity(my, 56, 64); my->toggleSwitch(); } } } } } if ( my->leverStatus == 4 ) { //Power on any neighbors that don't have power. my->switchUpdateNeighbors(); //TODO: Alternatively, instead of using CPU cycles on this, have the recursive network shutdown alert any switches connected to it that are powered on that it's shutting down, so that they can repower the network come next frame. } } else { my->flags[NOUPDATE] = true; } // Rotate the switch when it is on/off. if ( my->leverStatus == 0 ) { if ( my->roll > -PI / 4 ) { my->roll -= std::max<real_t>((my->roll + PI / 4) / 2, .05); } else { my->roll = -PI / 4; } } else if (my->leverStatus == 1 ) // 1/3 of the way up { if ( my->roll < -PI / 12 ) { my->roll += std::max<real_t>(-(my->roll + PI / 12) / 8, .02); } else { my->roll = -PI / 12; } } else if ( my->leverStatus == 2 ) // 2/3 of the way up { if ( my->roll < PI / 12 ) { my->roll += std::max<real_t>(-(my->roll - PI / 12) / 8, .02); } else { my->roll = PI / 12; } } else if ( my->leverStatus == 3 ) // all the way up { if ( my->roll < PI / 4 ) { my->roll += std::max<real_t>(-(my->roll - PI / 4) / 4, .02); } else { my->roll = PI / 4; if ( multiplayer != CLIENT ) { my->leverStatus = 4; serverUpdateEntitySkill(my, 1); } } } else if ( my->leverStatus == 4 ) // ticking down { if ( my->roll > -PI / 12 ) { my->roll -= (PI / 3) / static_cast<real_t>(my->leverTimerTicks); // move slowly towards 2/3rds of the resting point if ( my->ticks % 10 == 0 ) { playSoundEntityLocal(my, 247, 32); } } else { my->roll = -PI / 12; if ( multiplayer != CLIENT ) { playSoundEntity(my, 56, 64); my->leverStatus = 0; serverUpdateEntitySkill(my, 1); my->toggleSwitch(); } } } }
void actBoulder(Entity *my) { int i; my->skill[2]=-7; // invokes actEmpty() on clients my->flags[UPDATENEEDED] = TRUE; bool noground=FALSE; int x = std::min<int>(std::max(0,(int)(my->x/16)),map.width); int y = std::min<int>(std::max(0,(int)(my->y/16)),map.height); Uint32 index = y*MAPLAYERS+x*MAPLAYERS*map.height; if( !map.tiles[index] || animatedtiles[map.tiles[index]] ) noground = TRUE; // gravity bool nobounce=TRUE; if( !BOULDER_NOGROUND ) if( noground ) BOULDER_NOGROUND = TRUE; if( my->z < 0 || BOULDER_NOGROUND ) { my->vel_z = std::min(my->vel_z+.1,3.0); nobounce = TRUE; if( my->z >= 128 ) { list_RemoveNode(my->mynode); return; } if( !BOULDER_NOGROUND ) { if( my->z >= -8 && fabs(my->vel_z) > 2 ) { node_t *node; for( node=map.entities->first; node!=NULL; node=node->next ) { Entity *entity = (Entity *)node->element; if( entity == my ) continue; if( boulderCheckAgainstEntity(my,entity) ) return; } } } } else { if( fabs(my->vel_z) > 1 ) { playSoundEntity(my,182,128); my->vel_z = -(my->vel_z/2); nobounce = TRUE; } else { if( my->vel_z ) playSoundEntity(my,182,128); my->vel_z = 0; nobounce = FALSE; } my->z = 0; } my->z += my->vel_z; if( nobounce ) { if( !my->flags[PASSABLE] ) { my->flags[PASSABLE] = TRUE; if( multiplayer==SERVER ) serverUpdateEntityFlag(my,PASSABLE); } if( !BOULDER_STOPPED ) { my->x += my->vel_x; my->y += my->vel_y; double dist = sqrt(pow(my->vel_x,2)+pow(my->vel_y,2)); my->pitch += dist*.06; my->roll = PI/2; } } else if( !BOULDER_STOPPED ) { if( my->flags[PASSABLE] ) { my->flags[PASSABLE] = FALSE; if( multiplayer==SERVER ) serverUpdateEntityFlag(my,PASSABLE); } // horizontal velocity my->vel_x += cos(my->yaw)*.1; my->vel_y += sin(my->yaw)*.1; if( my->vel_x > 1.5 ) my->vel_x = 1.5; if( my->vel_x < -1.5 ) my->vel_x = -1.5; if( my->vel_y > 1.5 ) my->vel_y = 1.5; if( my->vel_y < -1.5 ) my->vel_y = -1.5; int x = std::min<int>(std::max(0.0,(my->x+cos(my->yaw)*8)/16),map.width-1); int y = std::min<int>(std::max(0.0,(my->y+sin(my->yaw)*8)/16),map.height-1); if( map.tiles[OBSTACLELAYER+y*MAPLAYERS+x*MAPLAYERS*map.height] ) { playSoundEntity(my,181,128); BOULDER_STOPPED = 1; } else { my->x += my->vel_x; my->y += my->vel_y; double dist = sqrt(pow(my->vel_x,2)+pow(my->vel_y,2)); my->pitch += dist*.06; my->roll = PI/2; // crush objects if( dist && !BOULDER_NOGROUND ) { node_t *node; for( node=map.entities->first; node!=NULL; node=node->next ) { Entity *entity = (Entity *)node->element; if( entity == my ) continue; if( boulderCheckAgainstEntity(my,entity) ) return; } } } } // pushing boulders if( BOULDER_STOPPED ) { if( !BOULDER_ROLLING ) { for(i=0;i<MAXPLAYERS;i++) { if( (i==0 && selectedEntity==my) || (client_selected[i]==my) ) { if(inrange[i]) { if( statGetSTR(stats[i])<5 ) { messagePlayer(i,language[456]); } else { if( players[i] ) { playSoundEntity(my, 151, 128); BOULDER_ROLLING=1; my->x = floor(my->x/16)*16+8; my->y = floor(my->y/16)*16+8; BOULDER_DESTX=(int)(my->x/16)*16+8; BOULDER_DESTY=(int)(my->y/16)*16+8; if( (int)(players[i]->x/16) < (int)(my->x/16) ) { BOULDER_ROLLDIR=0; // east } else if( (int)(players[i]->y/16) < (int)(my->y/16) ) { BOULDER_ROLLDIR=1; // south } else if( (int)(players[i]->x/16) > (int)(my->x/16) ) { BOULDER_ROLLDIR=2; // west } else if( (int)(players[i]->y/16) > (int)(my->y/16) ) { BOULDER_ROLLDIR=3; // north } switch( BOULDER_ROLLDIR ) { case 0: BOULDER_DESTX += 16; break; case 1: BOULDER_DESTY += 16; break; case 2: BOULDER_DESTX -= 16; break; case 3: BOULDER_DESTY -= 16; break; } } } } } } } else { switch( BOULDER_ROLLDIR ) { case 0: my->vel_x=1; my->vel_y=0; break; case 1: my->vel_x=0; my->vel_y=1; break; case 2: my->vel_x=-1; my->vel_y=0; break; case 3: my->vel_x=0; my->vel_y=-1; break; } int x = (my->x+my->vel_x*8)/16; int y = (my->y+my->vel_y*8)/16; x = std::min<unsigned int>(std::max(0,x),map.width-1); y = std::min<unsigned int>(std::max(0,y),map.height-1); if( map.tiles[OBSTACLELAYER+y*MAPLAYERS+x*MAPLAYERS*map.height] ) { BOULDER_ROLLING = 0; } else { my->x += my->vel_x; my->y += my->vel_y; double dist = sqrt(pow(my->vel_x,2)+pow(my->vel_y,2)); my->pitch += dist*.06; if( BOULDER_ROLLDIR==0 ) { if( my->x>=BOULDER_DESTX ) { my->x=BOULDER_DESTX; BOULDER_ROLLING=0; } } else if( BOULDER_ROLLDIR==1 ) { if( my->y>=BOULDER_DESTY ) { my->y=BOULDER_DESTY; BOULDER_ROLLING=0; } } else if( BOULDER_ROLLDIR==2 ) { if( my->x<=BOULDER_DESTX ) { my->x=BOULDER_DESTX; BOULDER_ROLLING=0; } } else if( BOULDER_ROLLDIR==3 ) { if( my->y<=BOULDER_DESTY ) { my->y=BOULDER_DESTY; BOULDER_ROLLING=0; } } double dir = my->yaw - BOULDER_ROLLDIR*PI/2; while( dir >= PI ) dir -= PI*2; while( dir < -PI ) dir += PI*2; my->yaw -= dir/16; while( my->yaw < 0 ) my->yaw += 2*PI; while( my->yaw >= 2*PI ) my->yaw -= 2*PI; // crush objects if( dist && !BOULDER_NOGROUND ) { node_t *node; for( node=map.entities->first; node!=NULL; node=node->next ) { Entity *entity = (Entity *)node->element; if( entity == my ) continue; if( boulderCheckAgainstEntity(my,entity) ) return; } } } } } // wrap around angles while( my->pitch >= PI*2 ) my->pitch -= PI*2; while( my->pitch < 0 ) my->pitch += PI*2; while( my->roll >= PI*2 ) my->roll -= PI*2; while( my->roll < 0 ) my->roll += PI*2; // rolling sound if( !BOULDER_STOPPED && (fabs(my->vel_x)>0 || fabs(my->vel_y)>0) ) { BOULDER_AMBIENCE++; if( BOULDER_AMBIENCE>=TICKS_PER_SECOND/3 ) { BOULDER_AMBIENCE=0; playSoundEntity(my, 151, 128); } } }
void actItem(Entity* my) { Item* item; int i; if ( multiplayer == CLIENT ) { my->flags[NOUPDATE] = true; if ( ITEM_LIFE == 0 ) { Entity* tempEntity = uidToEntity(clientplayer); if ( tempEntity ) { if ( entityInsideEntity(my, tempEntity) ) { my->parent = tempEntity->getUID(); } else { node_t* node; for ( node = map.creatures->first; node != nullptr; node = node->next ) { Entity* entity = (Entity*)node->element; if ( entity->behavior == &actPlayer || entity->behavior == &actMonster ) { if ( entityInsideEntity(my, entity) ) { my->parent = entity->getUID(); break; } } } } } else { node_t* node; for ( node = map.creatures->first; node != nullptr; node = node->next ) { Entity* entity = (Entity*)node->element; if ( entity->behavior == &actPlayer || entity->behavior == &actMonster ) { if ( entityInsideEntity(my, entity) ) { my->parent = entity->getUID(); break; } } } } } // request entity update (check if I've been deleted) if ( ticks % (TICKS_PER_SECOND * 5) == my->getUID() % (TICKS_PER_SECOND * 5) ) { strcpy((char*)net_packet->data, "ENTE"); net_packet->data[4] = clientnum; SDLNet_Write32(my->getUID(), &net_packet->data[5]); net_packet->address.host = net_server.host; net_packet->address.port = net_server.port; net_packet->len = 9; sendPacketSafe(net_sock, -1, net_packet, 0); } } else { // select appropriate model my->skill[2] = -5; if ( my->itemSokobanReward != 1 ) { my->flags[INVISIBLE] = false; } item = newItemFromEntity(my); my->sprite = itemModel(item); free(item); } //if( ITEM_LIFE==0 ) // playSoundEntityLocal( my, 149, 64 ); ITEM_LIFE++; /*ITEM_AMBIENCE++; if( ITEM_AMBIENCE>=TICKS_PER_SECOND*30 ) { ITEM_AMBIENCE=0; playSoundEntityLocal( my, 149, 64 ); }*/ // pick up item if (multiplayer != CLIENT) { if ( my->isInteractWithMonster() ) { Entity* monsterInteracting = uidToEntity(my->interactedByMonster); if ( monsterInteracting ) { if ( my->skill[10] >= 0 && my->skill[10] < NUMITEMS ) { if ( items[my->skill[10]].category == Category::FOOD && monsterInteracting->getMonsterTypeFromSprite() != SLIME ) { monsterInteracting->monsterConsumeFoodEntity(my, monsterInteracting->getStats()); } else { monsterInteracting->monsterAddNearbyItemToInventory(monsterInteracting->getStats(), 24, 9, my); } } my->clearMonsterInteract(); return; } my->clearMonsterInteract(); } for ( i = 0; i < MAXPLAYERS; i++) { if ((i == 0 && selectedEntity == my) || (client_selected[i] == my)) { if (inrange[i]) { if (players[i] != nullptr && players[i]->entity != nullptr) { playSoundEntity( players[i]->entity, 35 + rand() % 3, 64 ); } Item* item2 = newItemFromEntity(my); if ( players[i] && players[i]->entity ) { if ( my->itemStolen == 1 && item2 && (static_cast<Uint32>(item2->ownerUid) == players[i]->entity->getUID()) ) { steamAchievementClient(i, "BARONY_ACH_REPOSSESSION"); } } //messagePlayer(i, "old owner: %d", item2->ownerUid); if (item2) { item = itemPickup(i, item2); if (item) { if (i == 0) { free(item2); } int oldcount = item->count; item->count = 1; messagePlayer(i, language[504], item->description()); item->count = oldcount; if (i != 0) { free(item); } my->removeLightField(); list_RemoveNode(my->mynode); return; } } } } } } if ( my->itemNotMoving ) { switch ( my->sprite ) { case 610: case 611: case 612: case 613: my->spawnAmbientParticles(80, my->sprite - 4, 10 + rand() % 40, 1.0, false); if ( !my->light ) { my->light = lightSphereShadow(my->x / 16, my->y / 16, 3, 192); } break; default: break; } if ( multiplayer == CLIENT ) { // let the client process some more gravity and make sure it isn't stopping early at an awkward angle. if ( my->itemNotMovingClient == 1 ) { return; } } else { return; } } // gravity bool onground = false; if ( my->z < 7.5 - models[my->sprite]->sizey * .25 ) { // fall // chakram and shuriken lie flat, needs to use sprites for client if ( my->sprite == 567 || my->sprite == 569 ) { // todo: adjust falling rates for thrown items if need be ITEM_VELZ += 0.04; my->z += ITEM_VELZ; my->roll += 0.08; } else { ITEM_VELZ += 0.04; my->z += ITEM_VELZ; my->roll += 0.04; } } else { if ( my->x >= 0 && my->y >= 0 && my->x < map.width << 4 && my->y < map.height << 4 ) { if ( map.tiles[(int)(my->y / 16)*MAPLAYERS + (int)(my->x / 16)*MAPLAYERS * map.height] || (my->sprite >= 610 && my->sprite <= 613) ) { // land ITEM_VELZ *= -.7; if ( ITEM_VELZ > -.35 ) { // chakram and shuriken lie flat, needs to use sprites for client if ( my->sprite == 567 || my->sprite == 569 ) { my->roll = PI; my->pitch = 0; if ( my->sprite == 569 ) { my->z = 8.5 - models[my->sprite]->sizey * .25; } else { my->z = 8.75 - models[my->sprite]->sizey * .25; } } else { my->roll = PI / 2.0; my->z = 7.5 - models[my->sprite]->sizey * .25; } ITEM_VELZ = 0; onground = true; } else { onground = true; my->z = 7.5 - models[my->sprite]->sizey * .25 - .0001; } } else { // fall ITEM_VELZ += 0.04; my->z += ITEM_VELZ; my->roll += 0.04; } } else { // fall ITEM_VELZ += 0.04; my->z += ITEM_VELZ; my->roll += 0.04; } } // falling out of the map if ( my->z > 128 ) { if ( ITEM_TYPE == ARTIFACT_MACE && my->parent != 0 ) { steamAchievementEntity(uidToEntity(my->parent), "BARONY_ACH_STFU"); } list_RemoveNode(my->mynode); return; } // don't perform unneeded computations on items that have basically no velocity double groundheight; if ( my->sprite == 569 ) { groundheight = 8.5 - models[my->sprite]->sizey * .25; } else if ( my->sprite == 567 ) { groundheight = 8.75 - models[my->sprite]->sizey * .25; } else { groundheight = 7.5 - models[my->sprite]->sizey * .25; } if ( onground && my->z > groundheight - .0001 && my->z < groundheight + .0001 && fabs(ITEM_VELX) < 0.02 && fabs(ITEM_VELY) < 0.02 ) { my->itemNotMoving = 1; my->flags[UPDATENEEDED] = false; if ( multiplayer != CLIENT ) { serverUpdateEntitySkill(my, 18); //update itemNotMoving flag } else { my->itemNotMovingClient = 1; } return; } // horizontal motion if ( ITEM_NOCOLLISION ) { double newx = my->x + ITEM_VELX; double newy = my->y + ITEM_VELY; if ( !checkObstacle( newx, newy, my, NULL ) ) { my->x = newx; my->y = newy; my->yaw += sqrt( ITEM_VELX * ITEM_VELX + ITEM_VELY * ITEM_VELY ) * .05; } } else { double result = clipMove(&my->x, &my->y, ITEM_VELX, ITEM_VELY, my); my->yaw += result * .05; if ( result != sqrt( ITEM_VELX * ITEM_VELX + ITEM_VELY * ITEM_VELY ) ) { if ( !hit.side ) { ITEM_VELX *= -.5; ITEM_VELY *= -.5; } else if ( hit.side == HORIZONTAL ) { ITEM_VELX *= -.5; } else { ITEM_VELY *= -.5; } } } ITEM_VELX = ITEM_VELX * .925; ITEM_VELY = ITEM_VELY * .925; }