/* * XEvil(TM) Copyright (C) 1994,2000 Steve Hardt and Michael Judge * http://www.xevil.com * satan@xevil.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program, the file "gpl.txt"; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA, or visit http://www.gnu.org. */ // "actual.cpp" // Actual objects to be instantiated. #include "stdafx.h" #if X11 #ifndef NO_PRAGMAS #pragma implementation "actual.h" #endif #endif // Include Files #if X11 #include using namespace std; #endif #if WIN32 #include #include "resource.h" #endif #include "utils.h" #include "coord.h" #include "world.h" #include "physical.h" #include "actual.h" #include "sound_cmn.h" // Bitmap files. Also contain constants and some simple methods. // Most importantly, they contain the "contexts". #include "bitmaps/phys_mover/phys_mover.bitmaps" #include "bitmaps/explosion/explosion.bitmaps" #include "bitmaps/fire/fire.bitmaps" #include "bitmaps/fire_explosion/fire_explosion.bitmaps" #include "bitmaps/n_protection/n_protection.bitmaps" #include "bitmaps/t_protection/t_protection.bitmaps" #include "bitmaps/x_protection/x_protection.bitmaps" #include "bitmaps/shell/shell.bitmaps" #include "bitmaps/swap_shell/swap_shell.bitmaps" #include "bitmaps/lance/lance.bitmaps" #include "bitmaps/laser/laser.bitmaps" #include "bitmaps/frog_shell/frog_shell.bitmaps" #include "bitmaps/fireball/fireball.bitmaps" #include "bitmaps/missile/missile.bitmaps" #include "bitmaps/star/star.bitmaps" #include "bitmaps/blood/blood.bitmaps" #include "bitmaps/green_blood/green_blood.bitmaps" #include "bitmaps/oil_droplet/oil_droplet.bitmaps" #include "bitmaps/trapdoor/trapdoor.bitmaps" #include "bitmaps/home/home.bitmaps" #include "bitmaps/grenade/grenade.bitmaps" #include "bitmaps/napalm/napalm.bitmaps" #include "bitmaps/egg/egg.bitmaps" #include "bitmaps/xit/xit.bitmaps" #include "bitmaps/flag/flag.bitmaps" #include "bitmaps/rock/rock.bitmaps" #include "bitmaps/weight/weight.bitmaps" #include "bitmaps/altar_of_sin/altar_of_sin.bitmaps" #include "bitmaps/doppel/doppel.bitmaps" #include "bitmaps/cloak/cloak.bitmaps" #include "bitmaps/transmogifier/transmogifier.bitmaps" #include "bitmaps/med_kit/med_kit.bitmaps" #include "bitmaps/crack_pipe/crack_pipe.bitmaps" #include "bitmaps/caffine/caffine.bitmaps" #include "bitmaps/p_c_p/p_c_p.bitmaps" #include "bitmaps/n_shield/n_shield.bitmaps" #include "bitmaps/t_shield/t_shield.bitmaps" #include "bitmaps/bomb/bomb.bitmaps" #include "bitmaps/chainsaw/chainsaw.bitmaps" #include "bitmaps/pistol/pistol.bitmaps" #include "bitmaps/m_gun/m_gun.bitmaps" #include "bitmaps/lancer/lancer.bitmaps" #include "bitmaps/auto_lancer/auto_lancer.bitmaps" #include "bitmaps/f_thrower/f_thrower.bitmaps" #include "bitmaps/launcher/launcher.bitmaps" #include "bitmaps/grenades/grenades.bitmaps" #include "bitmaps/napalms/napalms.bitmaps" #include "bitmaps/stars/stars.bitmaps" #include "bitmaps/swapper/swapper.bitmaps" #include "bitmaps/frog_gun/frog_gun.bitmaps" #include "bitmaps/dog_whistle/dog_whistle.bitmaps" #include "bitmaps/demon_summoner/demon_summoner.bitmaps" #include "bitmaps/enforcer/enforcer.bitmaps" #include "bitmaps/frog/frog.bitmaps" #include "bitmaps/hero/hero.bitmaps" #include "bitmaps/ninja/ninja.bitmaps" #include "bitmaps/alien/alien.bitmaps" #include "bitmaps/hugger/hugger.bitmaps" #include "bitmaps/chopper_boy/chopper_boy.bitmaps" #include "bitmaps/seal/seal.bitmaps" #include "bitmaps/fire_demon/fire_demon.bitmaps" #include "bitmaps/dragon/dragon.bitmaps" #include "bitmaps/walker/walker.bitmaps" #include "bitmaps/dog/dog.bitmaps" #include "bitmaps/yeti/yeti.bitmaps" ///// Some helper macros to define creatures with certain abilities. #define DEFINE_CREATURE_CTORS_2(CLASSNAME,ABILITY0,ability0,ABILITY1,ability1) \ CLASSNAME::CLASSNAME(WorldP w,LocatorP l,const Pos &rawPos) \ : Creature(creatureContext,creatureXdata, \ new PtrList(new ABILITY0(ability0 ## Context,ability0 ## Xdata), \ new ABILITY1(ability1 ## Context,ability1 ## Xdata)), \ w,l,rawPos) {} \ \ CLASSNAME::CLASSNAME(InStreamP inStream,WorldP w,LocatorP l) \ : Creature(inStream,creatureContext,creatureXdata, \ w,l) {} #define DEFINE_CREATURE_CTORS_3(CLASSNAME,ABILITY0,ability0,ABILITY1,ability1,ABILITY2,ability2) \ CLASSNAME::CLASSNAME(WorldP w,LocatorP l,const Pos &rawPos) \ : Creature(creatureContext,creatureXdata, \ new PtrList(new ABILITY0(ability0 ## Context,ability0 ## Xdata), \ new ABILITY1(ability1 ## Context,ability1 ## Xdata), \ new ABILITY2(ability2 ## Context,ability2 ## Xdata)), \ w,l,rawPos) {} \ \ CLASSNAME::CLASSNAME(InStreamP inStream,WorldP w,LocatorP l) \ : Creature(inStream,creatureContext,creatureXdata, \ w,l) {} #define DEFINE_CREATURE_CTORS_4(CLASSNAME,ABILITY0,ability0,ABILITY1,ability1,ABILITY2,ability2,ABILITY3,ability3) \ CLASSNAME::CLASSNAME(WorldP w,LocatorP l,const Pos &rawPos) \ : Creature(creatureContext,creatureXdata, \ new PtrList(new ABILITY0(ability0 ## Context,ability0 ## Xdata), \ new ABILITY1(ability1 ## Context,ability1 ## Xdata), \ new ABILITY2(ability2 ## Context,ability2 ## Xdata), \ new ABILITY3(ability3 ## Context,ability3 ## Xdata)), \ w,l,rawPos) {} \ \ CLASSNAME::CLASSNAME(InStreamP inStream,WorldP w,LocatorP l) \ : Creature(inStream,creatureContext,creatureXdata, \ w,l) {} #define DEFINE_CREATURE_CTORS_5(CLASSNAME,ABILITY0,ability0,ABILITY1,ability1,ABILITY2,ability2,ABILITY3,ability3,ABILITY4,ability4) \ CLASSNAME::CLASSNAME(WorldP w,LocatorP l,const Pos &rawPos) \ : Creature(creatureContext,creatureXdata, \ new PtrList(new ABILITY0(ability0 ## Context,ability0 ## Xdata), \ new ABILITY1(ability1 ## Context,ability1 ## Xdata), \ new ABILITY2(ability2 ## Context,ability2 ## Xdata), \ new ABILITY3(ability3 ## Context,ability3 ## Xdata), \ new ABILITY4(ability4 ## Context,ability4 ## Xdata)), \ w,l,rawPos) {} \ \ CLASSNAME::CLASSNAME(InStreamP inStream,WorldP w,LocatorP l) \ : Creature(inStream,creatureContext,creatureXdata, \ w,l) {} PhysMover::PhysMover(WorldP w,LocatorP l,MoverP mover) : Physical(context,w,l) { moverId = mover->get_mover_id(); areaPrev = mover->get_area(); // state = AfterUpdate; state = BeforeAct; } PhysMover::PhysMover(InStreamP in,WorldP w,LocatorP l) : Physical(in,context,w,l) { state = AfterUpdate; _update_from_stream(in); } DEFINE_UPDATE_FROM_STREAM(PhysMover,Physical) void PhysMover::_update_from_stream(InStreamP in) { moverId.read(in); areaPrev.read(in); WorldP world = get_world(); MoverP mover = world->lookup(moverId); if (mover) { mover->update_from_stream(areaPrev); } } int PhysMover::get_write_length() { return Physical::get_write_length() + Identifier::get_write_length() + // moverId Area::get_write_length(); // area } void PhysMover::write(OutStreamP out) { Physical::write(out); // Not always true. Is true after a few turns? // if (state != AfterUpdate) { // cerr << "PhysMover: state should be AfterUpdate when write() is called." // << endl; //} Area area = get_area(); Area areaNext = get_area_next(); if (!(area == areaNext)) { cerr << "PhysMover: area should equal areaNext when write() is called." << endl; } moverId.write(out); area.write(out); } DEFINE_CREATE_FROM_STREAM(PhysMover) const Area &PhysMover::get_area() { if (state == AfterUpdate) return later_area(); else return earlier_area(); } const Area &PhysMover::get_area_next() { if (state == BeforeAct) return earlier_area(); else return later_area(); } Boolean PhysMover::collidable() { return False; } Boolean PhysMover::corporeal_attack(PhysicalP,int) { return False; } void PhysMover::heat_attack(PhysicalP,int,Boolean) {} void PhysMover::act() { // assert(state == BeforeAct); Fails at new level. state = BetweenActUpdate; } void PhysMover::update() { assert(state == BetweenActUpdate); state = AfterUpdate; } // Don't actually draw, Locator will draw the background for it. void PhysMover::draw(CMN_DRAWABLE,Xvars &,int,const Area &) { } void PhysMover::init_x(Xvars &,IXCommand,void*) { } const Area &PhysMover::earlier_area() { WorldP world = get_world(); MoverP mover = world->lookup(moverId); if (mover) { areaPrev = mover->get_area() - mover->get_vel(); } // Just to return something. return areaPrev; } const Area &PhysMover::later_area() { WorldP world = get_world(); MoverP mover = world->lookup(moverId); if (mover) { return mover->get_area(); } // Just to return something. return areaPrev; } void PhysMover::mover_clock() { // assert(state == AfterUpdate); // Don't assert, actually two mover_clock() calls before first act() call. state = BeforeAct; } Explosion::Explosion(WorldP w,LocatorP l,const Pos &mid, const Id &bomb_er,int r,int dMax) : Physical(context,w,l) { bomber = bomb_er; Pos pos; pos.x = mid.x - EXPLOSION_VISIBLE_RADIUS; pos.y = mid.y - EXPLOSION_VISIBLE_RADIUS; Size size; size.width = size.height = 2 * EXPLOSION_VISIBLE_RADIUS; Area narea(AR_RECT,pos,size); area = narea; middle = mid; radius = r; damageMax = dMax; } Explosion::Explosion(InStreamP in,WorldP w,LocatorP l) : Physical(in,context,w,l) { damageMax = 0; _update_from_stream(in); } DEFINE_UPDATE_FROM_STREAM(Explosion,Physical) void Explosion::_update_from_stream(InStreamP in) { area.read(in); middle.read(in); radius = in->read_short(); } int Explosion::get_write_length() { return Physical::get_write_length() + Area::get_write_length() + // area Pos::get_write_length() + // middle sizeof(short); // radius } void Explosion::write(OutStreamP out) { Physical::write(out); area.write(out); middle.write(out); assert(radius <= USHRT_MAX); out->write_short((u_short)radius); } DEFINE_CREATE_FROM_STREAM(Explosion) Boolean Explosion::collidable() { return False; } const Area &Explosion::get_area() { return area; } const Area &Explosion::get_area_next() { return area; } void Explosion::act() { PhysicalP nearby[OL_NEARBY_MAX]; int nitems; LocatorP locator = get_locator(); locator->get_nearby(nearby,nitems,this,radius); PhysicalP p = locator->lookup(bomber); int hit = 0; for (int n = 0; n < nitems; n++) { // Perhaps should check that nearby[n] is mapped. if (nearby[n]->collidable()) { const Area &otherArea = nearby[n]->get_area(); int distance = middle.distance(otherArea.get_middle()); assert (distance >= 0); nearby[n]->corporeal_attack(p,damageMax*(radius-distance)/radius); hit++; } } ostrstream msg; msg << "Explosion hits " << hit << " objects." << ends; locator->message_enq(msg.str()); kill_self(); Physical::act(); } void Explosion::init_x(Xvars &,IXCommand,void*) { } Fire::Fire(WorldP w,LocatorP l,const Pos &mid,Boolean coll) : Physical(context,w,l) { Pos pos; pos.x = mid.x - FIRE_DIAMETER_INIT / 2; pos.y = mid.y - FIRE_DIAMETER_INIT / 2; Size size; size.width = size.height = FIRE_DIAMETER_INIT; Area nArea(AR_RECT,pos,size); area = areaNext = nArea; isCollidable = coll; } Fire::Fire(InStreamP in,WorldP w,LocatorP l) : Physical(in,context,w,l) { isCollidable = False; _update_from_stream(in); } DEFINE_UPDATE_FROM_STREAM(Fire,Physical) void Fire::_update_from_stream(InStreamP in) { area.read(in); areaNext = area; } int Fire::get_write_length() { return Physical::get_write_length() + Area::get_write_length(); } void Fire::write(OutStreamP out) { Physical::write(out); assert(area == areaNext); area.write(out); } DEFINE_CREATE_FROM_STREAM(Fire) int Fire::get_damage() { return FIRE_DAMAGE; } const Area &Fire::get_area() { return area; } const Area &Fire::get_area_next() { return areaNext; } int Fire::get_drawing_level() { // Draw like everything else for now. return 1; } Boolean Fire::collidable() { return isCollidable; } void Fire::avoid(PhysicalP){ } void Fire::collide(PhysicalP other) { if (other->is_shot()) return; other->heat_attack(NULL,FIRE_HEAT,True); } Boolean Fire::corporeal_attack(PhysicalP,int) { return False; } void Fire::heat_attack(PhysicalP,int,Boolean) { } void Fire::act() { Pos pos; Size size; area.get_rect(pos,size); size.width -= (FIRE_RADIUS_DELTA * 2); size.height = size.width; if (size.width <= 0) { // Will be gone before next draw. kill_self(); } else { pos.x += FIRE_RADIUS_DELTA; pos.y += FIRE_VEL_Y; Area nArea(AR_RECT,pos,size); areaNext = nArea; } Physical::act(); } void Fire::update() { area = areaNext; Physical::update(); } FireExplosion::FireExplosion(WorldP w,LocatorP l, const Pos &rawPos,const Id &sh) : Physical(context,w,l) { shooter = sh; Area narea(AR_RECT,rawPos,Fireball::get_size()); area = narea; timer.set(F_THROWER_FIRE_TIME); } FireExplosion::FireExplosion(InStreamP in,WorldP w,LocatorP l) : Physical(in,context,w,l) { timer.set(1); _update_from_stream(in); } DEFINE_UPDATE_FROM_STREAM(FireExplosion,Physical) void FireExplosion::_update_from_stream(InStreamP in) { area.read(in); } int FireExplosion::get_write_length() { return Physical::get_write_length() + Area::get_write_length(); // area } void FireExplosion::write(OutStreamP out) { Physical::write(out); area.write(out); } DEFINE_CREATE_FROM_STREAM(FireExplosion) Boolean FireExplosion::collidable() { return False; } Boolean FireExplosion::corporeal_attack(PhysicalP,int) { return False; } void FireExplosion::heat_attack(PhysicalP,int,Boolean) { } const Area &FireExplosion::get_area() { return area; } const Area &FireExplosion::get_area_next() { return area; } void FireExplosion::draw(CMN_DRAWABLE,Xvars &,int,const Area &) { // Don't draw at all. } void FireExplosion::act() { if (timer.ready()) { kill_self(); } // Create fireballs going in both directions. else { WorldP world = get_world(); LocatorP locator = get_locator(); PhysicalP left = new Fireball(world,locator,area.get_pos(),shooter,CO_L); PhysicalP right = new Fireball(world,locator,area.get_pos(),shooter,CO_R); assert(left && right); locator->add(left); locator->add(right); } timer.clock(); Physical::act(); } void FireExplosion::init_x(Xvars &,IXCommand,void*) { } NProtection::NProtection(WorldP w,LocatorP l,const Area &area) : Protection(context,xdata,w,l,area) { n = N_PROTECTION_N; } CONSTRUCTOR_LEAF_IO(NProtection,Protection) { n = 0; } DEFINE_CREATE_FROM_STREAM(NProtection) Boolean NProtection::corporeal_protect(int) { assert(n >= 0); if (n <= 0) { return False; } n--; if (n == 0 ) { kill_self(); } return True; } Boolean NProtection::heat_protect(int,Boolean) { assert(n >= 0); return False; } void NProtection::init_x(Xvars &xvars,IXCommand command,void* arg) { Protection::init_x(xvars,command,arg,context,xdata); } TProtection::TProtection(WorldP w,LocatorP l,const Area &area) : Protection(context,xdata,w,l,area) { timer.set(T_PROTECTION_TIME); } CONSTRUCTOR_LEAF_IO(TProtection,Protection) { } DEFINE_CREATE_FROM_STREAM(TProtection) void TProtection::act() { if (timer.ready()) { kill_self(); } timer.clock(); Protection::act(); } Boolean TProtection::corporeal_protect(int) { return True; } Boolean TProtection::heat_protect(int,Boolean) { return True; } void TProtection::init_x(Xvars &xvars,IXCommand command,void* arg) { Protection::init_x(xvars,command,arg,context,xdata); } XProtection::XProtection(WorldP w,LocatorP l,const Area &area) : Protection(context,xdata,w,l,area) { health = X_PROTECTION_HEALTH; } CONSTRUCTOR_LEAF_IO(XProtection,Protection) { } DEFINE_CREATE_FROM_STREAM(XProtection) Boolean XProtection::corporeal_protect(int damage) { health -= damage; if (health <= 0) { kill_self(); } return True; } Boolean XProtection::heat_protect(int heat,Boolean) { // Approximate the amount of damage the heat attack would do. int damage = heat * Fire::get_damage(); return XProtection::corporeal_protect(damage); } void XProtection::init_x(Xvars &xvars,IXCommand command,void* arg) { Protection::init_x(xvars,command,arg,context,xdata); } Rock::Rock(WorldP w,LocatorP l,const Pos &pos) : Liftable(context,xdata,w,l,pos) { } CONSTRUCTOR_LEAF_IO(Rock,Liftable) { } DEFINE_CREATE_FROM_STREAM(Rock) void Rock::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.heavyContext.fallingContext.movingContext, xdata); } Weight::Weight(WorldP w,LocatorP l,const Pos &pos) : Liftable(context,xdata,w,l,pos) {} CONSTRUCTOR_LEAF_IO(Weight,Liftable) { } DEFINE_CREATE_FROM_STREAM(Weight) void Weight::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.heavyContext.fallingContext.movingContext, xdata); } DoubleSpeed::DoubleSpeed(int timed) : Doubler(timed) { multiplier = 1.5f; increment = 0.5f; } ModifierId DoubleSpeed::get_modifier_id() { return M_DoubleSpeed; } int DoubleSpeed::apply(Attribute attr,int val) { switch (attr) { case ACCELERATION: case AIR_SPEED: case CENTER_SPEED: case CLIMB_SPEED: case CRAWL_SPEED: val = (int)(val * multiplier); break; }; if (next) return next->apply(attr,val); else return val; } ModifierId DoubleJump::get_modifier_id() { return M_DoubleJump; } int DoubleJump::apply(Attribute attr,int val) { switch (attr) { // Side effect that flying creatures get an extra boost, but AltarOfSin // doesn't give DoubleJump to Flying creatures. case AIR_SPEED: case JUMP: val = (int)(val * multiplier); break; } if (next) return next->apply(attr,val); else return val; } ModifierId DoubleHealth::get_modifier_id() { return M_DoubleHealth; } int DoubleHealth::apply(Attribute attr,int val) { if (attr == HEALTH_MAX) { val = (int)(val * multiplier); } if (next) return next->apply(attr,val); else return val; } AltarOfSin::AltarOfSin(WorldP w,LocatorP l,const Pos &pos) : Heavy(context,xdata,w,l,pos) { turnTaken = False; } CONSTRUCTOR_LEAF_IO(AltarOfSin,Heavy) { turnTaken = False; } DEFINE_CREATE_FROM_STREAM(AltarOfSin) Boolean AltarOfSin::corporeal_attack(PhysicalP other,int) { /* Don't fuck with the Altar of Sin. */ if (turnTaken) { return False; } IntelP intel; if (other && (other->get_class_id() != A_AltarOfSin) && (intel = other->get_intel())) { LocatorP locator = get_locator(); ostrstream str; // Turned into a frog/baby seal. if (Utils::coin_flip() && other->is_moving() && // Don't frog a frog, or something that has already been morphed. other->get_class_id() != A_Frog && other->get_class_id() != A_Seal && !other->get_ability(AB_Morphed)) { Boolean doFrog = Utils::coin_flip(); str << intel->get_name() << " attacks the Altar of Sin and is " << "turned into a " << (doFrog ? "frog" : "baby-seal") << "." << ends; locator->message_enq(str.str()); other->set_intel(NULL); if (!other->get_mapped()) { cerr << "Warning:AltarOfSin::corporeal_attack: object " << "should be mapped." << endl; } other->set_mapped_next(False); const Area &area = other->get_area(); PhysicalP morph; if (doFrog) { Pos pos = area.get_middle() - 0.5f * Frog::get_size_max(); morph = new Frog(get_world(),locator,pos); } else { Pos pos = area.get_middle() - 0.5f * Seal::get_size_max(); morph = new Seal(get_world(),locator,pos); } assert(morph); // Give frog the morphed ability, so it will eventually turn back. AbilityP morphed = new Morphed(FrogShell::morphedContext,Morphed::defaultXdata,other); ((CreatureP)morph)->add_ability(morphed); morph->set_intel(intel); locator->add(morph); } // Lose all health. else { str << "BLASPHMER! " << intel->get_name() << " loses health for daring to attack the Altar of Sin." << ends; locator->message_enq(str.str()); int damage = other->get_health(); other->corporeal_attack(this,damage); } locator->arena_message_enq(Utils::strdup("Don't FUCK with the Altar of Sin"), other); turnTaken = True; } return True; } void AltarOfSin::heat_attack(PhysicalP,int,Boolean){ } void AltarOfSin::collide(PhysicalP other) { IntelP intel; if (!turnTaken && other->is_creature() && (intel = other->get_intel()) && intel->is_human()) { int lives = intel->get_lives(); LocatorP locator = get_locator(); ostrstream msg; ostrstream arenaMsg; // Choose different blessings to give. int n = 0; Boolean ok = False; while (!ok && n < ALTAR_OF_SIN_TRIES) { n++; ok = True; ModifierP mod; /*** Appending to the Intel's or Creature's modifier list. ***/ ModifierP modList = intel->get_modifiers(); assert(modList); ModifierP pModList = ((CreatureP)other)->get_modifiers(); assert(pModList); int which = Utils::choose(9); switch (which) { // extra life or extra kills. case 0: // If human has infinite lives, give him some points(kills) instead. if (lives == IT_INFINITE_LIVES) { #if 0 for (int m = 0; m < ALTAR_OF_SIN_KILLS; m++) { intel->add_human_kill(); } msg << intel->get_name() << " sells soul for " << ALTAR_OF_SIN_KILLS << " kills." << ends; arenaMsg << "You Gain " << ALTAR_OF_SIN_KILLS << " Kills" << ends; #endif // Everybody hates the "You Gain 5 Kills" thing. So, I removed it. ok = False; } else { intel->set_lives(lives + 1); msg << intel->get_name() << " sells soul for an extra life." << ends; arenaMsg << "You Sold Your Soul For an Extra Life" << ends; } break; // double speed case 1: mod = new DoubleSpeed(); assert(mod); modList->append_unique(mod); msg << intel->get_name() << " sells soul for Double Speed." << ends; arenaMsg << "Double Speed" << ends; break; // extra jump case 2: if (other->get_ability(AB_Flying) || other->get_ability(AB_Grounded)) { // extra jump is useless or nearly so. ok = False; break; } mod = new DoubleJump(); assert(mod); modList->append_unique(mod); msg << intel->get_name() << " sells soul for extra jumping powers." << ends; arenaMsg << "Extra Jumping Powers" << ends; break; // double maximum health case 3: mod = new DoubleHealth(); assert(mod); // Add to Creature's modifiers because of problems with // initializing health when transferred from one physical // to another. pModList->append_unique(mod); // So max health takes effect immediately. other->heal(); msg << intel->get_name() << " sells soul for Double Health." << ends; arenaMsg << "Double Health" << ends; break; // Healing powers case 4: { // Already has healing powers. if (other->get_ability(AB_Healing)) { ok = False; break; } AbilityP h = new Healing(Hero::healingContext,Healing::defaultXdata); // Already checked that other is a Creature(). ((CreatureP)other)->add_ability(h); // Might as well make sure they can take advantage of it now. other->heal(); msg << intel->get_name() << " sells soul for Healing Powers." << ends; arenaMsg << "Healing Powers" << ends; } break; // HellFire case 5: { // Already has OnFire powers. if (other->get_ability(AB_OnFire)) { ok = False; break; } AbilityP a = new OnFire(FireDemon::onFireContext,OnFire::defaultXdata); // Already checked that other is a Creature(). ((CreatureP)other)->add_ability(a); msg << intel->get_name() << " sells soul for HellFire Powers." << ends; arenaMsg << "HellFire Powers" << ends; } break; // Fireballs case 6: { // Already has BuiltIn weapon if (other->get_ability(AB_BuiltIn)) { ok = False; break; } // Don't want to be BuiltIn and Fighter at the same time. AbilityP fighter = other->get_ability(AB_Fighter); if (fighter) { ((CreatureP)other)->remove_ability(AB_Fighter); } AbilityP a = new BuiltIn(FireDemon::builtInContext,BuiltIn::defaultXdata); // Already checked that other is a Creature(). ((CreatureP)other)->add_ability(a); msg << intel->get_name() << " sells soul for Fireballs." << ends; arenaMsg << "Fireballs" << ends; } break; // Flying powers case 7: { // Already has flying powers. if (other->get_ability(AB_Flying)) { ok = False; break; } // Remove the existing Locomotion LocomotionP l = other->get_locomotion(); if (l) { ((CreatureP)other)->remove_ability(l->get_ability_id()); } // else something is wrong. AbilityP a = new Flying(ChopperBoy::flyingContext,Flying::defaultXdata); // Already checked that other is a Creature(). ((CreatureP)other)->add_ability(a); msg << intel->get_name() << " sells soul for Flying Powers." << ends; arenaMsg << "Flying Powers" << ends; } break; // Sticky ability case 8: { // Already has sticky. // Also, don't kill off Flying to replace with Sticky. if (other->get_ability(AB_Sticky) || other->get_ability(AB_Flying)) { ok = False; break; } // Has to have all the bitmaps required for Sticky, e.g. // climbing on the walls and ceiling. if (!Sticky::has_required_frames(((MovingP)other)->get_moving_context())) { ok = False; break; } // Remove the existing Locomotion LocomotionP l = other->get_locomotion(); if (l) { ((CreatureP)other)->remove_ability(l->get_ability_id()); } // else something is wrong. AbilityP a = new Sticky(Ninja::stickyContext,Sticky::defaultXdata); // Already checked that other is a Creature(). ((CreatureP)other)->add_ability(a); msg << intel->get_name() << " sells soul for Sticky Powers." << ends; arenaMsg << "Sticky Powers" << ends; } break; default: ok = False; // try again. } } // while locator->message_enq(msg.str()); locator->arena_message_enq(arenaMsg.str(),other); kill_self(); turnTaken = True; } else { Heavy::collide(other); } } void AltarOfSin::update() { turnTaken = False; Heavy::update(); } void AltarOfSin::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.fallingContext.movingContext, xdata); } Doppel::Doppel(WorldP w,LocatorP l,const Pos &pos) : AutoUse(context,xdata,w,l,pos) {} CONSTRUCTOR_LEAF_IO(Doppel,AutoUse) { } DEFINE_CREATE_FROM_STREAM(Doppel) void Doppel::use(PhysicalP p) { assert(p && p->get_intel()); // If p is not allowed to use doppelgangers, just return. const PhysicalContext *usersPC = p->get_context(); if (!usersPC->doppelUser) { return; } stats.add_use(); WorldP world = get_world(); LocatorP locator = get_locator(); IntelP masterIntel = p->get_intel(); PhysicalP obj = create_physical(p->get_area(),p); NeutralP neutral = new DoppelIntel(world,locator,"Doppel", &DoppelIntel::suggestedOptions,DoppelIntel::suggestedMask, masterIntel); assert(neutral); locator->register_neutral(neutral); locator->add(obj); obj->set_intel(neutral); // obj->set_dont_collide(p); // Use the dont_collide functions of the intel, not the physical. neutral->set_dont_collide(masterIntel->get_intel_id()); kill_self(); Item::use(p); } // New version which uses the PhysicalContext create function // More general and easy to extend. PhysicalP Doppel::create_physical(const Area &area,PhysicalP original) { WorldP world = get_world(); LocatorP locator = get_locator(); Pos middle = area.get_middle(); PhysicalP obj; // Get the PhysicalContext(description of its class) of the original const PhysicalContext *originalContext = original->get_context(); // Use the original's PhysicalContext to create a new object. obj = originalContext->create(originalContext->arg,world,locator, middle - 0.5f * originalContext->sizeMax); assert(obj); return obj; } void Doppel::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.itemContext.fallingContext.movingContext, xdata); } Stats Doppel::stats; Cloak::Cloak(WorldP w,LocatorP l,const Pos &pos) : Item(context,xdata,w,l,pos) {} CONSTRUCTOR_LEAF_IO(Cloak,Item) { } DEFINE_CREATE_FROM_STREAM(Cloak) void Cloak::use(PhysicalP p) { assert(p->is_moving()); // Destroy any protection p may be using. LocatorP l = get_locator(); PhysicalP prot = l->lookup(((MovingP)p)->get_protection()); if (prot) { prot->kill_self(); } // Only make invisible if the Object supports invisibilty. // Don't bother doing anything on an already invisible user. if (((MovingP)p)->supports_invisibility() && !((MovingP)p)->is_invisible()) { ((MovingP)p)->set_invisible_next(PH_INVISIBLE_TIME); // So p will stop being invisible when p attacks something. ((MovingP)p)->set_attack_hook(attack_hook); stats.add_use(); } kill_self(); Item::use(p); } void Cloak::attack_hook(PhysicalP p) { assert(p->is_moving()); // Means turn it off. ((MovingP)p)->set_invisible_next(-1); } void Cloak::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.fallingContext.movingContext, xdata); } Stats Cloak::stats; Transmogifier::Transmogifier(WorldP w,LocatorP l,const Pos &pos) : AutoUse(context,xdata,w,l,pos) {} CONSTRUCTOR_LEAF_IO(Transmogifier,AutoUse) { } DEFINE_CREATE_FROM_STREAM(Transmogifier) void Transmogifier::use(PhysicalP other) { assert(other->alive()); stats.add_use(); IntelP intel = other->get_intel(); // Must check that other does not have protection against swapping. if (intel && !other->swap_protect()) { LocatorP locator = get_locator(); const Area &area = other->get_area(); // Don't transmogify something into a new object of the same class // it already is. That's boring. PhysicalP p = new_physical(area.get_middle(),other->get_class_id()); locator->add(p); p->set_intel(intel); other->set_intel(NULL); other->drop_all(); other->set_quiet_death(); other->kill_self(); } kill_self(); AutoUse::use(other); } // New version which uses Locator::filter_contexts. // More general and easy to extend. PhysicalP Transmogifier::new_physical(const Pos &middle,ClassId notThis) { const PhysicalContext *list[A_CLASSES_NUM]; int size; LocatorP l = get_locator(); WorldP w = get_world(); // Get list of all classes that are potential transmogify targets. size = l->filter_contexts(list,NULL,transmogify_target_filter, (void*)notThis); // Something is wrong if size == 0, because there are no classes to // transmogify into. assert(size); // Randomly choose. int which = Utils::choose(size); // Create object centered on middle. Pos pos = middle - 0.5f * list[which]->sizeMax; // Make sure a create function exists. assert(list[which]->create); PhysicalP obj = list[which]->create(list[which]->arg,w,l,pos); return obj; } Boolean Transmogifier::transmogify_target_filter(const PhysicalContext *pc, void *closure) { // notThis is used to prevent transmogifying into the same class an object // already is. ClassId notThis = (intptr_t)closure; if (pc->classId == notThis) { return False; } return pc->transmogifyTarget; } void Transmogifier::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.itemContext.fallingContext.movingContext, xdata); } Stats Transmogifier::stats; MedKit::MedKit(WorldP w,LocatorP l,const Pos &pos) : AutoUse(context,xdata,w,l,pos) { } CONSTRUCTOR_LEAF_IO(MedKit,AutoUse) { } DEFINE_CREATE_FROM_STREAM(MedKit) void MedKit::use(PhysicalP p) { stats.add_use(); assert(p->alive()); p->heal(); kill_self(); AutoUse::use(p); } void MedKit::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.itemContext.fallingContext.movingContext, xdata); } Stats MedKit::stats; Crack::Crack() : Modifier(CRACK_TIME) {} ModifierId Crack::get_modifier_id() { return M_Crack; } int Crack::apply(Attribute attr,int val) { switch (attr) { case ACCELERATION: case AIR_SPEED: case CENTER_SPEED: case CLIMB_SPEED: case CRAWL_SPEED: // Using *= gives warnings on some compilers. val = (int)(val * 2.0); break; case JUMP: val = (int)(val * 1.5); break; case DAMAGE: val = val * 2; break; case HIGH: val = 1; // Creature is high. break; }; if (next) return next->apply(attr,val); else return val; } CrackPipe::CrackPipe(WorldP w,LocatorP l,const Pos &pos) : Drugs(context,xdata,w,l,pos) {} CONSTRUCTOR_LEAF_IO(CrackPipe,Drugs) { } DEFINE_CREATE_FROM_STREAM(CrackPipe) ModifierP CrackPipe::create_modifier() { stats.add_use(); ModifierP crack = new Crack(); assert(crack); return crack; } void CrackPipe::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.autoUseContext.itemContext.fallingContext.movingContext, xdata); } Stats CrackPipe::stats; CaffineM::CaffineM() : Modifier(CAFFINE_M_TIME) {} ModifierId CaffineM::get_modifier_id() { return M_CaffineM; } int CaffineM::apply(Attribute attr,int val) { switch (attr) { case ACCELERATION: case AIR_SPEED: case CENTER_SPEED: case CLIMB_SPEED: case CRAWL_SPEED: // Using *= gives warnings on some compilers. val = (int)(val * 1.4); break; case JUMP: val = (int)(val * 1.3); break; case HIGH: val = 1; // Creature is high. break; }; if (next) return next->apply(attr,val); else return val; } Caffine::Caffine(WorldP w,LocatorP l,const Pos &pos) : Drugs(context,xdata,w,l,pos) {} CONSTRUCTOR_LEAF_IO(Caffine,Drugs) { } DEFINE_CREATE_FROM_STREAM(Caffine) ModifierP Caffine::create_modifier() { stats.add_use(); ModifierP m = new CaffineM(); assert(m); return m; } void Caffine::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.autoUseContext.itemContext.fallingContext.movingContext, xdata); } Stats Caffine::stats; PCPM::PCPM() : Modifier(PCP_M_TIME) {} ModifierId PCPM::get_modifier_id() { return M_PCPM; } int PCPM::apply(Attribute attr,int val) { switch (attr) { case HIGH: val = 1; // Creature is high. break; case CORPOREAL_ATTACK: if (val > 0) { // Turn damage into "superficial damage". val = -val; } break; case DAMAGE: val = val * 2; break; case HEAT_ATTACK: val = 0; break; }; if (next) { return next->apply(attr,val); } else { return val; } } void PCPM::preDie(PhysicalP p) { // Whack off half of health. p->corporeal_attack(NULL,p->get_health() / 2); } PCP::PCP(WorldP w,LocatorP l,const Pos &pos) : Drugs(context,xdata,w,l,pos) { } CONSTRUCTOR_LEAF_IO(PCP,Drugs) { } DEFINE_CREATE_FROM_STREAM(PCP) ModifierP PCP::create_modifier() { stats.add_use(); ModifierP m = new PCPM(); assert(m); return m; } void PCP::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.autoUseContext.itemContext.fallingContext.movingContext, xdata); } Stats PCP::stats; NShield::NShield(WorldP w,LocatorP l,const Pos &pos) : Shield(context,xdata,w,l,pos) { } CONSTRUCTOR_LEAF_IO(NShield,Shield) { } DEFINE_CREATE_FROM_STREAM(NShield); void NShield::use(PhysicalP p) { stats.add_use(); Shield::use(p); } ProtectionP NShield::create_protection(const Area &area) { ProtectionP pr = new NProtection(get_world(),get_locator(),area); assert(pr); return pr; } void NShield::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg, context.autoUseContext.itemContext.fallingContext.movingContext, xdata); } Stats NShield::stats; TShield::TShield(WorldP w,LocatorP l,const Pos &pos) : Shield(context,xdata,w,l,pos) { } CONSTRUCTOR_LEAF_IO(TShield,Shield) { } DEFINE_CREATE_FROM_STREAM(TShield) void TShield::use(PhysicalP p) { stats.add_use(); Shield::use(p); } ProtectionP TShield::create_protection(const Area &area) { ProtectionP pr = new TProtection(get_world(),get_locator(),area); assert(pr); return pr; } void TShield::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg, context.autoUseContext.itemContext.fallingContext.movingContext, xdata); } Stats TShield::stats; Bomb::Bomb(WorldP w,LocatorP l,const Pos &p) : Animated(context,xdata,w,l,p,BOMB_FRAME_INACTIVE) { // set_frame(BOMB_FRAME_INACTIVE); // set_frame_next(BOMB_FRAME_INACTIVE); frame = BOMB_FRAME_INACTIVE; Timer ntimer(BOMB_TIME); timer = ntimer; active = False; defused = False; } CONSTRUCTOR_LEAF_IO(Bomb,Animated) { active = False; defused = False; } DEFINE_CREATE_FROM_STREAM(Bomb) Boolean Bomb::is_bomb() { return True; } void Bomb::use(PhysicalP bomberP) { if (!active) { frame = BOMB_FRAME_ACTIVE; timer.set(); active = True; if (bomberP) { bomber = bomberP->get_id(); } LocatorP locator = get_locator(); #if 0 // Don't send the 4,3,2,etc to to message bar anymore. ostrstream msg; msg << frame << ends; locator->message_enq(msg.str()); #endif set_cant_take(); } Animated::use(bomberP); } void Bomb::act() { if (active) timer.clock(); if (active && timer.ready()) { if (! frame) { kill_self(); } else { timer.set(); frame--; #if 0 LocatorP locator = get_locator(); ostrstream msg; msg << frame << ends; locator->message_enq(msg.str()); #endif } } set_frame_next(frame); Animated::act(); } void Bomb::set_quiet_death() { defused = True; Animated::set_quiet_death(); } void Bomb::die() { if (!defused) { // Kind of a hack, recording bombs that exploded, not bombs that // were used. stats.add_use(); WorldP world = get_world(); LocatorP locator = get_locator(); const Area area = get_area(); PhysicalP explosion = new Explosion(world,locator,area.get_middle(),bomber, BOMB_EXPLOSION_RADIUS,BOMB_EXPLOSION_DAMAGE_MAX); assert (explosion); locator->add(explosion); } Animated::die(); } void Bomb::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg, context.itemContext.fallingContext.movingContext, xdata); } Stats Bomb::stats; Trapdoor::Trapdoor(WorldP w,LocatorP l,const Pos &pos,const Id &home_id) : Moving(context,xdata,w,l,pos) { Timer nTimer(TRAPDOOR_TIME); timer = nTimer; timer.set(); sealsNum = 0; homeId = home_id; } CONSTRUCTOR_LEAF_IO(Trapdoor,Moving) { sealsNum = 0; } DEFINE_CREATE_FROM_STREAM(Trapdoor) Boolean Trapdoor::collidable() { return False; } int Trapdoor::get_drawing_level() { return 0; } void Trapdoor::append_seals_out(int &num,IntelId *lems) { // num starts out at the correct initial value. for (int i = 0; i < sealsNum; i++, num++) { lems[num] = seals[i]; } } void Trapdoor::act() { if (timer.ready() && sealsNum < SEALS_MAX) { LocatorP l = get_locator(); WorldP w = get_world(); const Area &area = get_area(); Pos pos = area.get_middle(); Size sealSize = Seal::get_size_max(); pos.x -= sealSize.width / 2; pos.y += sealSize.height; PhysicalP seal = new Seal(w,l,pos); assert(seal); char sealStr[20]; ostrstream str(sealStr,20); str << "seal-" << sealsNum << ends; NeutralP sealIntel = new SealIntel(w,l,sealStr,homeId); seal->set_intel(sealIntel); l->register_neutral(sealIntel); seals[sealsNum] = sealIntel->get_intel_id(); l->add(seal); sealsNum++; timer.set(); } timer.clock(); Moving::act(); } void Trapdoor::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context, xdata); } Home::Home(WorldP w,LocatorP l,const Pos &pos) : Moving(context,xdata,w,l,pos) { sealsSafe = 0; } CONSTRUCTOR_LEAF_IO(Home,Moving) { sealsSafe = 0; } DEFINE_CREATE_FROM_STREAM(Home) Boolean Home::collidable() { return False; } int Home::get_drawing_level() { return 0; } void Home::act() { PhysicalP nearby[OL_NEARBY_MAX]; int nItems; LocatorP locator = get_locator(); locator->get_nearby(nearby,nItems,this,HOME_RADIUS); for (int n = 0; n < nItems; n++) { IntelP intel = nearby[n]->get_intel(); if (intel && intel->is_seal_intel()) { nearby[n]->set_quiet_death(); nearby[n]->kill_self(); sealsSafe++; } } Moving::act(); } void Home::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context, xdata); } Shell::Shell(WorldP w,LocatorP l,const Pos &p,const Id &shooter, Dir d) : Shot(context,xdata,w,l,p,shooter,d) { stats.add_creation(); } CONSTRUCTOR_LEAF_IO(Shell,Shot) { } DEFINE_CREATE_FROM_STREAM(Shell) void Shell::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } Stats Shell::stats; SwapShell::SwapShell(WorldP w,LocatorP l,const Pos &p, const Id &sh,const Id &sw,Dir d) : Shot(context,xdata,w,l,p,sh,d) { swapper = sw; stats.add_creation(); } CONSTRUCTOR_LEAF_IO(SwapShell,Shot) { } DEFINE_CREATE_FROM_STREAM(SwapShell) void SwapShell::collide(PhysicalP other) { if (other->is_shot()) { return; } LocatorP locator = get_locator(); const Id &shooter = get_shooter(); PhysicalP shooterP; // Do nothing if shooter no longer exists. if (!other->swap_protect() && (shooterP = locator->lookup(shooter))) { // Swap souls. IntelP tmp = other->get_intel(); /* Don't swap unless both shooter and other have intelligence. */ if (tmp && shooterP->get_intel()) { other->set_intel(shooterP->get_intel()); shooterP->set_intel(tmp); // Destroy swapper. PhysicalP swapperP = locator->lookup(swapper); if (swapperP) { swapperP->kill_self(); } } } kill_self(); } void SwapShell::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } Stats SwapShell::stats; Lance::Lance(WorldP w,LocatorP l,const Pos &p,const Id &shooter,Dir d) : Shot(context,xdata,w,l,p,shooter,d,d) { stats.add_creation(); } CONSTRUCTOR_LEAF_IO(Lance,Shot) { } DEFINE_CREATE_FROM_STREAM(Lance) // This function is also used by Laser::compute_weapon_dir(). Dir Lance::compute_weapon_dir(ITcommand command) { switch (command) { case IT_WEAPON_R: return CO_R; case IT_WEAPON_DN_R: return CO_DN_R_R; case IT_WEAPON_DN: return CO_DN; case IT_WEAPON_DN_L: return CO_DN_L_L; case IT_WEAPON_L: return CO_L; case IT_WEAPON_UP_L: return CO_UP_L_L; case IT_WEAPON_UP: return CO_UP; case IT_WEAPON_UP_R: return CO_UP_R_R; }; return CO_air; } void Lance::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } Stats Lance::stats; Laser::Laser(WorldP w,LocatorP l,const Pos &p,const Id &shooter,Dir d) : Shot(context,xdata,w,l,p,shooter,d,d) { stats.add_creation(); } CONSTRUCTOR_LEAF_IO(Laser,Shot) { } DEFINE_CREATE_FROM_STREAM(Laser) Dir Laser::compute_weapon_dir(ITcommand command) { // Kind of a hack. return Lance::compute_weapon_dir(command); } void Laser::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext,xdata); } Stats Laser::stats; FrogShell::FrogShell(WorldP w,LocatorP l,const Pos &p, const Id &shooter,const Id &frog_gun,Dir d) : Shot(context,xdata,w,l,p,shooter,d) { frogGun = frog_gun; stats.add_creation(); } CONSTRUCTOR_LEAF_IO(FrogShell,Shot) { } DEFINE_CREATE_FROM_STREAM(FrogShell) void FrogShell::collide(PhysicalP other) { // Be careful not to frog a Frog, or something that is already morphed. if (other->is_shot() || other->get_class_id() == A_Frog || other->get_ability(AB_Morphed)) { return; } IntelP intel = other->get_intel(); if (!other->frog_protect() && intel && other->is_moving()) { other->set_intel(NULL); assert(other->get_mapped()); other->set_mapped_next(False); const Area &area = other->get_area(); Pos pos = area.get_middle() - 0.5f * Frog::get_size_max(); LocatorP locator = get_locator(); PhysicalP frog = new Frog(get_world(),locator,pos); assert(frog); // Give frog the morphed ability, so it will eventually turn back. AbilityP morphed = new Morphed(morphedContext,Morphed::defaultXdata,other); ((CreatureP)frog)->add_ability(morphed); frog->set_intel(intel); locator->add(frog); // Destroy frogGun. PhysicalP p; if (p = locator->lookup(frogGun)) { p->kill_self(); } } kill_self(); } void FrogShell::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } Stats FrogShell::stats; Fireball::Fireball(WorldP w,LocatorP l,const Pos &p,const Id &shooter, Dir d,int h,int t,Boolean hOnFire) : Shot(context,xdata,w,l,p,shooter,d) { if (t == -1) { timer.set(FIREBALL_TIME); } else { timer.set(t); } if (h == -1) { heat = FIREBALL_HEAT; } else { heat = h; } hurtOnFire = hOnFire; } CONSTRUCTOR_LEAF_IO(Fireball,Shot) { heat = 0; hurtOnFire = False; } DEFINE_CREATE_FROM_STREAM(Fireball) void Fireball::collide(PhysicalP other) { // Fireballs are not destroyed by shots. if (other->is_shot() && other->get_class_id() != A_Missile) { return; } LocatorP locator = get_locator(); PhysicalP p = locator->lookup(get_shooter()); // If we have the flag set to hurt Creatures with the OnFire // ability,do a one-time corporeal_attack(). if (hurtOnFire && other->get_ability(AB_OnFire)) { other->corporeal_attack(p,FIREBALL_BACKUP_PHYSICAL_DAMAGE); kill_self(); } else { other->heat_attack(p,heat); } } void Fireball::act() { if (timer.ready()) { kill_self(); } timer.clock(); Shot::act(); } void Fireball::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } Missile::Missile(WorldP w,LocatorP l,const Pos &p,const Id &sh,Dir d) : Shot(context,xdata,w,l,p,sh,d,d) { timer.set(MISSILE_DESTRUCT_TIME); Timer nTimer(MISSILE_ROTATE_TIME); rotate = nTimer; hasTarget = False; shooterId = sh; stats.add_creation(); } CONSTRUCTOR_LEAF_IO(Missile,Shot) { } DEFINE_CREATE_FROM_STREAM(Missile) void Missile::act() { LocatorP locator = get_locator(); // Find a target. if (!hasTarget) { PhysicalP nearby[OL_NEARBY_MAX]; int nearbyNum; // Should work even though this not fully constructed. locator->get_nearby(nearby,nearbyNum,this,MISSILE_RADIUS); // Search through list for best possible candidate. const Area &area = get_area(); Pos middle = area.get_middle(); int distance_2 = -1; // Best so far. IntelP nearbyIntel; PhysicalP shooter; IntelP shooterIntel; for (int n = 0; n < nearbyNum; n++) { if (nearby[n]->alive() && nearby[n]->is_creature() && // Don't go after invisible things. !(nearby[n]->is_moving() && ((MovingP)nearby[n])->is_invisible())&& // Don't go after shooter. nearby[n]->get_id() != shooterId && // Don't go after nearby if it is a slave to the shooter's intel // or the shooter is a slave to nearby. !( (shooter = locator->lookup(shooterId)) && (nearbyIntel = nearby[n]->get_intel()) && (shooterIntel = shooter->get_intel()) && ((!nearbyIntel->is_human() && ((MachineP)nearbyIntel)->get_master_intel_id() == shooterIntel->get_intel_id() ) || (!shooterIntel->is_human() && ((MachineP)shooterIntel)->get_master_intel_id() == nearbyIntel->get_intel_id() ) ) ) && // Don't go after members of shooters team !(shooter && locator->same_team(shooter,nearby[n]))) { const Area &area = nearby[n]->get_area(); int dist_2 = middle.distance_2(area.get_middle()); if (distance_2 == -1 || dist_2 < distance_2) { targetId = nearby[n]->get_id(); distance_2 = dist_2; hasTarget = True; } } // big if statement } // for n } // hasTarget if (rotate.ready()) { rotate.set(); PhysicalP target; if ((target = locator->lookup(targetId)) && // Lose sight of invisible objects. !(target->is_moving() && ((MovingP)target)->is_invisible())) { Boolean noChange = False; Dir dir = get_dir(); const Area &area = get_area(); const Area &targetArea = target->get_area(); Dir dirTo = area.dir_to(targetArea); if (dir > dirTo) { if (dir - dirTo > (CO_DIR_PURE / 2)) { dir++; } else { dir--; } } else if (dirTo > dir) { if (dirTo - dir > (CO_DIR_PURE / 2)) { dir--; } else { dir++; } } else { noChange = True; } if (!noChange) { if (dir >= CO_DIR_MAX) { dir -= CO_DIR_PURE; } if (dir < (CO_DIR_MAX - CO_DIR_PURE)) { dir += CO_DIR_PURE; } set_dir_next(dir); const Vel *unitVels = get_unit_vels(); set_vel_next((float)context.speed * unitVels[dir]); } } // good target } // rotate.ready() if (timer.ready()) { kill_self(); } timer.clock(); rotate.clock(); Shot::act(); } void Missile::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } Stats Missile::stats; Star::Star(WorldP w,LocatorP l,const Pos &p,const Id &sh,Dir d) : Shot(context,xdata,w,l,p,sh,d) { stats.add_creation(); } CONSTRUCTOR_LEAF_IO(Star,Shot) { } DEFINE_CREATE_FROM_STREAM(Star) void Star::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } Stats Star::stats; Blood::Blood(WorldP w,LocatorP l,const Pos &pos,const Vel &vel) : Droplet(context,xdata,w,l,pos,vel) {} CONSTRUCTOR_LEAF_IO(Blood,Droplet) { } DEFINE_CREATE_FROM_STREAM(Blood) PhysicalP Blood::create_and_add_if(WorldP w,LocatorP l,const Pos &p,const Vel &v) { PhysicalP blood = NULL; int index = get_available_entry(l); if (index != -1) { blood = new Blood(w,l,p,v); assert(blood); l->add(blood); set_entry(index,blood->get_id()); } return blood; } void Blood::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } GreenBlood::GreenBlood(WorldP w,LocatorP l,const Pos &pos,const Vel &vel) : Droplet(context,xdata,w,l,pos,vel) {} CONSTRUCTOR_LEAF_IO(GreenBlood,Droplet) { } DEFINE_CREATE_FROM_STREAM(GreenBlood) PhysicalP GreenBlood::create_and_add_if(WorldP w,LocatorP l,const Pos &p,const Vel &v) { PhysicalP blood = NULL; int index = get_available_entry(l); if (index != -1) { blood = new GreenBlood(w,l,p,v); assert(blood); l->add(blood); set_entry(index,blood->get_id()); } return blood; } void GreenBlood::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } OilDroplet::OilDroplet(WorldP w,LocatorP l,const Pos &pos,const Vel &vel) : Droplet(context,xdata,w,l,pos,vel) {} CONSTRUCTOR_LEAF_IO(OilDroplet,Droplet) { } DEFINE_CREATE_FROM_STREAM(OilDroplet) PhysicalP OilDroplet::create_and_add_if(WorldP w,LocatorP l,const Pos &p,const Vel &v) { PhysicalP blood = NULL; int index = get_available_entry(l); if (index != -1) { blood = new OilDroplet(w,l,p,v); assert(blood); l->add(blood); set_entry(index,blood->get_id()); } return blood; } void OilDroplet::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } Grenade::Grenade(WorldP w,LocatorP l,const Pos &pos,const Id &sh, Dir dir,Speed speed) : Falling(context,xdata,w,l,pos,dir) { assert(dir != CO_air); const Vel *unitVels = get_unit_vels(); Vel initVel = (float)speed * unitVels[dir]; set_vel(initVel); set_vel_next(initVel); timer.set(GRENADE_TIME); shooter = sh; defused = False; } Grenade::Grenade(WorldP w,LocatorP l,const Pos &pos,const Id &sh, const Vel &vel) : Falling(context,xdata,w,l,pos,vel.get_dir()) { stats.add_creation(); set_vel(vel); set_vel_next(vel); timer.set(GRENADE_TIME); shooter = sh; defused = False; } CONSTRUCTOR_LEAF_IO(Grenade,Falling) { defused = False; } DEFINE_CREATE_FROM_STREAM(Grenade) void Grenade::set_quiet_death() { defused = True; Falling::set_quiet_death(); } void Grenade::act() { if (timer.ready()) { kill_self(); } timer.clock(); Falling::act(); } void Grenade::die() { if (!defused) { WorldP world = get_world(); LocatorP locator = get_locator(); const Area area = get_area(); PhysicalP explosion = new Explosion(world,locator,area.get_middle(),shooter, GRENADE_EXPLOSION_RADIUS,GRENADE_EXPLOSION_DAMAGE_MAX); assert (explosion); locator->add(explosion); } Falling::die(); } void Grenade::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } Stats Grenade::stats; Napalm::Napalm(WorldP w,LocatorP l,const Pos &pos,const Id &sh, const Vel &vel) : Falling(context,xdata,w,l,pos,vel.get_dir()) { stats.add_creation(); set_vel(vel); set_vel_next(vel); timer.set(GRENADE_TIME); shooter = sh; defused = False; } CONSTRUCTOR_LEAF_IO(Napalm,Falling) { defused = False; } DEFINE_CREATE_FROM_STREAM(Napalm) void Napalm::set_quiet_death() { defused = True; Falling::set_quiet_death(); } // Don't want NapalmGrenades to set each other off. void Napalm::heat_attack(PhysicalP,int,Boolean) { } void Napalm::act() { if (timer.ready()) { kill_self(); } timer.clock(); Falling::act(); } void Napalm::die() { // Create a FireExplosion object to spit out fireballs. if (!defused) { WorldP world = get_world(); LocatorP locator = get_locator(); const Area area = get_area(); Pos pos = Coord::shot_initial_pos(area,CO_dn,Fireball::get_size(),CO_air); PhysicalP p = new FireExplosion(world,locator,pos,shooter); assert(p); locator->add(p); } Falling::die(); } void Napalm::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } Stats Napalm::stats; Egg::Egg(WorldP w,LocatorP l,const Pos &pos, const IntelOptions &ops,ITmask opMask) : Falling(context,xdata,w,l,pos) { intelOptions = ops; intelOpMask = opMask; open = False; } CONSTRUCTOR_LEAF_IO(Egg,Falling) { intelOpMask = ITnone; _update_from_stream(in); } DEFINE_UPDATE_FROM_STREAM(Egg,Falling) void Egg::_update_from_stream(InStreamP in) { open = (Boolean)in->read_char(); } int Egg::get_write_length() { return Falling::get_write_length() + sizeof(char); } void Egg::write(OutStreamP out) { Falling::write(out); out->write_char((char)open); } DEFINE_CREATE_FROM_STREAM(Egg) void Egg::act() { if (!open) { LocatorP l = get_locator(); PhysicalP nearby[OL_NEARBY_MAX]; int nItems; l->get_nearby(nearby,nItems,this,EGG_RADIUS); // Check for nearby humans. for (int n = 0; n < nItems; n++) { IntelP intel = nearby[n]->get_intel(); if (intel && intel->is_human() && !nearby[n]->get_ability(AB_Hugger) && nearby[n]->get_class_id() != A_Alien) // Trigger the egg. { PhysicalP hugger; IntelP intel; WorldP w = get_world(); // Hugger initial position is above the egg. const Area &area = get_area(); Pos eggMiddle = area.get_middle(); // Decide whether to create a red or green face hugger. Boolean gHugger = Utils::choose(EGG_GREEN_HUGGER_PERCENT) == 0; Size huggerSize = gHugger ? GreenHugger::get_size_max() : RedHugger::get_size_max(); Pos p(eggMiddle.x - huggerSize.width / 2, eggMiddle.y - EGG_HUGGER_EJECT_HEIGHT); // Create a Green or Red Hugger. if (gHugger) { // Create a GreenHugger, a neutral intelligence. hugger = new GreenHugger(w,l,p); intel = new Neutral(w,l,"Hugger", &intelOptions,intelOpMask); l->register_neutral((NeutralP)intel); } else { // Create a RedHugger, an enemy intelligence. hugger = new RedHugger(w,l,p); intel = new Enemy(w,l,"Hugger", &intelOptions,intelOpMask); l->register_enemy((EnemyP)intel); } assert(hugger); assert(intel); hugger->set_intel(intel); l->add(hugger); // Don't want Hugger to bump into the egg. hugger->set_dont_collide(this->get_id()); // Make egg open. open = True; set_dir_next(CO_center); // The pixmap for an open egg. break; } } } Falling::act(); } void Egg::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.movingContext, xdata); } Xit::Xit(WorldP w,LocatorP l,const Pos &pos) : Touchable(context,xdata,w,l,pos) {} CONSTRUCTOR_LEAF_IO(Xit,Touchable) { } DEFINE_CREATE_FROM_STREAM(Xit) void Xit::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.fallingContext.movingContext, xdata); } Flag::Flag(WorldP w,LocatorP l,const Pos &pos) : Touchable(context,xdata,w,l,pos) {} CONSTRUCTOR_LEAF_IO(Flag,Touchable) { } DEFINE_CREATE_FROM_STREAM(Flag) void Flag::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.fallingContext.movingContext, xdata); } Chainsaw::Chainsaw(WorldP w,LocatorP l,const Pos &p) : Cutter(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(Chainsaw,Cutter) { } DEFINE_CREATE_FROM_STREAM(Chainsaw) void Chainsaw::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } // This is here Pistol::Pistol(WorldP w,LocatorP l,const Pos &p) : Gun(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(Pistol,Gun) { } DEFINE_CREATE_FROM_STREAM(Pistol) void Pistol::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } MGun::MGun(WorldP w,LocatorP l,const Pos &p) : Gun(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(MGun,Gun) { } DEFINE_CREATE_FROM_STREAM(MGun) void MGun::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } Swapper::Swapper(WorldP w,LocatorP l,const Pos &p) : SingleGun(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(Swapper,SingleGun) { } DEFINE_CREATE_FROM_STREAM(Swapper) Size Swapper::get_shot_size(Dir) { return SwapShell::get_size(); } PhysicalP Swapper::create_shot(PhysicalP shooter,WorldP w,LocatorP l, const Pos &pos,Dir d) { PhysicalP swapShell = new SwapShell(w,l,pos,shooter->get_id(),get_id(),d); assert (swapShell); return swapShell; } void Swapper::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.gunContext.weaponContext.itemContext.fallingContext.movingContext, xdata); } Lancer::Lancer(WorldP w,LocatorP l,const Pos &p) : Gun(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(Lancer,Gun) { } DEFINE_CREATE_FROM_STREAM(Lancer) Size Lancer::get_shot_size(Dir dir) { return Lance::get_size(dir); } Dir Lancer::compute_weapon_dir(ITcommand command) { return Lance::compute_weapon_dir(command); } PhysicalP Lancer::create_shot(PhysicalP shooter,WorldP w,LocatorP l, const Pos &pos,Dir d) { PhysicalP lance = new Lance(w,l,pos,shooter->get_id(),d); assert (lance); return lance; } void Lancer::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } AutoLancer::AutoLancer(WorldP w,LocatorP l,const Pos &p) : Gun(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(AutoLancer,Gun) { } DEFINE_CREATE_FROM_STREAM(AutoLancer) Size AutoLancer::get_shot_size(Dir dir) { return Lance::get_size(dir); } Dir AutoLancer::compute_weapon_dir(ITcommand command) { return Lance::compute_weapon_dir(command); } PhysicalP AutoLancer::create_shot(PhysicalP shooter,WorldP w,LocatorP l, const Pos &pos,Dir d) { PhysicalP lance = new Lance(w,l,pos,shooter->get_id(),d); assert (lance); return lance; } void AutoLancer::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } FrogGun::FrogGun(WorldP w,LocatorP l,const Pos &p) : SingleGun(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(FrogGun,SingleGun) { } DEFINE_CREATE_FROM_STREAM(FrogGun) Size FrogGun::get_shot_size(Dir) { return FrogShell::get_size(); } PhysicalP FrogGun::create_shot(PhysicalP shooter,WorldP w,LocatorP l, const Pos &pos,Dir d) { PhysicalP shot = new FrogShell(w,l,pos,shooter->get_id(),get_id(),d); assert (shot); return shot; } void FrogGun::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.gunContext.weaponContext.itemContext.fallingContext.movingContext, xdata); } DogWhistle::DogWhistle(WorldP w,LocatorP l,const Pos &p) : Whistle(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(DogWhistle,Whistle) { } DEFINE_CREATE_FROM_STREAM(DogWhistle) PetP DogWhistle::create_pet(IntelP master,Boolean alternatePet,int n) { // The default pet is a Dog, alternate pet is a RedHugger, ha, ha. const Area &area = get_area(); Pos pos = area.get_middle() - 0.5f * (alternatePet ? RedHugger::get_size_max() : Dog::get_size_max()); // So multiple dogs don't pop up on top of each other. Size offset; offset.set(Utils::choose(21) - 3,Utils::choose(21) - 3); pos = pos + offset; LocatorP l = get_locator(); PhysicalP p; if (alternatePet) { p = new RedHugger(get_world(),get_locator(),pos); } else { if (n % 2 == 0) { p = new Dog(get_world(),get_locator(),pos); } else { p = new Mutt(get_world(),get_locator(),pos); } } assert(p); IntelOptions ops; ops.psychotic = True; ops.classFriends = False; // So Dogs attack enemy dogs. // Don't give a limited lifespan. PetP pet = new Pet(get_world(),get_locator(),"Spot",&ops, ITpsychotic | ITclassFriends,master); assert(pet); pet->set_dont_collide(master->get_intel_id()); p->set_intel(pet); l->add(p); l->register_neutral(pet); return pet; } void DogWhistle::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } DemonSummoner::DemonSummoner(WorldP w,LocatorP l,const Pos &p) : Whistle(context,xdata,w,l,p) { } CONSTRUCTOR_LEAF_IO(DemonSummoner,Whistle) { } DEFINE_CREATE_FROM_STREAM(DemonSummoner) PetP DemonSummoner::create_pet(IntelP master,Boolean,int) { // Doesn't use alternatePet const Area &area = get_area(); Pos pos = area.get_middle() - 0.5f * FireDemon::get_size_max(); // Don't bother offseting randomly for multiple Demons. LocatorP l = get_locator(); PhysicalP p = new FireDemon(get_world(),get_locator(),pos); assert(p); IntelOptions ops; ops.psychotic = True; ops.classFriends = False; // If summon multiple demons. ops.limitedLifespan = DEMON_SUMMONER_SUICIDE_TIME; PetP pet = new Pet(get_world(),get_locator(),"WormChewer",&ops, ITpsychotic | ITclassFriends | ITlimitedLifespan,master); assert(pet); pet->set_dont_collide(master->get_intel_id()); p->set_intel(pet); l->add(p); l->register_neutral(pet); return pet; } void DemonSummoner::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } FThrower::FThrower(WorldP w,LocatorP l,const Pos &p) : Gun(context,xdata,w,l,p) { Timer nTimer(F_THROWER_FIRE_TIME); stopFiring = nTimer; isFiring = False; } CONSTRUCTOR_LEAF_IO(FThrower,Gun) { } DEFINE_CREATE_FROM_STREAM(FThrower) PhysicalP FThrower::create_shot(PhysicalP shooter,WorldP w,LocatorP l, const Pos &pos,Dir d) { PhysicalP fireball = new Fireball(w,l,pos,shooter->get_id(),d); assert (fireball); return fireball; } void FThrower::fire(const Id &id,ITcommand command) { killerId = id; fireCommand = command; stopFiring.set(); isFiring = True; } void FThrower::act() { if (isFiring && stopFiring.ready()) isFiring = False; if (isFiring) Gun::fire(killerId,fireCommand); stopFiring.clock(); Gun::act(); } Size FThrower::get_shot_size(Dir) { return Fireball::get_size(); } void FThrower::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } Launcher::Launcher(WorldP w,LocatorP l,const Pos &p) : Gun(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(Launcher,Gun) { } DEFINE_CREATE_FROM_STREAM(Launcher) Size Launcher::get_shot_size(Dir dir) { return Missile::get_size(dir); } PhysicalP Launcher::create_shot(PhysicalP shooter,WorldP world, LocatorP locator,const Pos &pos,Dir dir) { PhysicalP shot = new Missile(world,locator,pos,shooter->get_id(),dir); assert (shot); return shot; } void Launcher::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } Grenades::Grenades(WorldP w,LocatorP l,const Pos &p) : Gun(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(Grenades,Gun) { } DEFINE_CREATE_FROM_STREAM(Grenades) Size Grenades::get_shot_size(Dir dir) { return Grenade::get_size(dir); } PhysicalP Grenades::create_shot(PhysicalP shooter,WorldP world, LocatorP locator,const Pos &pos,Dir dir) { Speed speed; if (dir == CO_UP_L || dir == CO_UP || dir == CO_UP_R) { speed = GRENADES_TOSS_SPEED; } else if (dir == CO_R || dir == CO_DN_R || dir == CO_DN_L || dir == CO_L) { speed = GRENADES_ROLL_SPEED; } else if (dir == CO_DN) { speed = 0; } else { assert(0); } assert(shooter->is_moving()); const Vel *unitVels = get_unit_vels(); Vel vel = ((MovingP)shooter)->get_vel() + (float)speed * unitVels[dir]; // PhysicalP shot = new Grenade(world,locator,pos,shooter->get_id(),dir,speed); PhysicalP shot = new Grenade(world,locator,pos,shooter->get_id(),vel); assert (shot); return shot; } void Grenades::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } Napalms::Napalms(WorldP w,LocatorP l,const Pos &p) : Gun(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(Napalms,Gun) { } DEFINE_CREATE_FROM_STREAM(Napalms) Size Napalms::get_shot_size(Dir dir) { return Napalm::get_size(dir); } // Copied from Grenades, should have common parent. PhysicalP Napalms::create_shot(PhysicalP shooter,WorldP world, LocatorP locator,const Pos &pos,Dir dir) { // Using same parameters as grenades. Speed speed; if (dir == CO_UP_L || dir == CO_UP || dir == CO_UP_R) speed = GRENADES_TOSS_SPEED; else if (dir == CO_R || dir == CO_DN_R || dir == CO_DN_L || dir == CO_L) speed = GRENADES_ROLL_SPEED; else if (dir == CO_DN) speed = 0; else assert(0); assert(shooter->is_moving()); const Vel *unitVels = get_unit_vels(); Vel vel = ((MovingP)shooter)->get_vel() + (float)speed * unitVels[dir]; // PhysicalP shot = new Grenade(world,locator,pos,shooter->get_id(),dir,speed); PhysicalP shot = new Napalm(world,locator,pos,shooter->get_id(),vel); assert (shot); return shot; } void Napalms::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } Stars::Stars(WorldP w,LocatorP l,const Pos &p) : Gun(context,xdata,w,l,p) {} CONSTRUCTOR_LEAF_IO(Stars,Gun) { } DEFINE_CREATE_FROM_STREAM(Stars) Size Stars::get_shot_size(Dir) { return Star::get_size(); } PhysicalP Stars::create_shot(PhysicalP shooter,WorldP world,LocatorP locator, const Pos &pos,Dir dir) { PhysicalP shot = new Star(world,locator,pos,shooter->get_id(),dir); assert (shot); return shot; } void Stars::fire(const Id &shooterId,ITcommand command) { LocatorP locator = get_locator(); PhysicalP shooter = locator->lookup(shooterId); if (shooter) { // Ninjas firing stars give a wider spread. int delta = (shooter->get_class_id() == A_Ninja) ? 2 : 1; // Fire a spread of shots around center. Dir center = Intel::command_weapon_to_dir_8(command); if (center != CO_air) for (Dir baseDir = center - delta; baseDir <= center + delta; baseDir++) { Dir dir; if (baseDir >= CO_DIR_MAX) dir = baseDir - CO_DIR_PURE; else if (baseDir < CO_DIR_MAX - CO_DIR_PURE) dir = baseDir + CO_DIR_PURE; else dir = baseDir; // Only set the timer on the last shot. Boolean lastOne = (baseDir == center + delta); Gun::_fire(shooterId,dir,lastOne,lastOne); } } } void Stars::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,context.weaponContext.itemContext.fallingContext.movingContext, xdata); } DEFINE_CREATURE_CTORS_3(Enforcer,Grounded,grounded,Suicide,suicide,Prickly,prickly) DEFINE_CREATE_FROM_STREAM(Enforcer) void Enforcer::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } Stats Enforcer::stats; DEFINE_CREATURE_CTORS_2(Frog,Suicide,suicide,Hopping,hopping) DEFINE_CREATE_FROM_STREAM(Frog) void Frog::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } Stats Frog::stats; // Temporarily disable Hero's lifting ability until we have time to finish it // properly. //DEFINE_CREATURE_CTORS_5(Hero,User,user,Fighter,fighter,Walking,walking,Healing,healing,Lifter,lifter) DEFINE_CREATURE_CTORS_4(Hero,User,user,Fighter,fighter,Walking,walking,Healing,healing) DEFINE_CREATE_FROM_STREAM(Hero) void Hero::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } Stats Hero::stats; DEFINE_CREATURE_CTORS_3(Ninja,User,user,Fighter,fighter,Sticky,sticky) DEFINE_CREATE_FROM_STREAM(Ninja) void Ninja::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } Stats Ninja::stats; DEFINE_CREATURE_CTORS_3(Alien,Sticky,sticky,Prickly,prickly,Healing,healing) DEFINE_CREATE_FROM_STREAM(Alien) void Alien::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } Stats Alien::stats; DEFINE_CREATURE_CTORS_2(RedHugger,Sticky,sticky,Hugger,hugger) DEFINE_CREATE_FROM_STREAM(RedHugger) void RedHugger::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } DEFINE_CREATURE_CTORS_2(GreenHugger,Sticky,sticky,Hugger,hugger) DEFINE_CREATE_FROM_STREAM(GreenHugger) void GreenHugger::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } DEFINE_CREATURE_CTORS_3(ChopperBoy,User,user,Flying,flying,Lifter,lifter) DEFINE_CREATE_FROM_STREAM(ChopperBoy) void ChopperBoy::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } Stats ChopperBoy::stats; DEFINE_CREATURE_CTORS_3(Seal,Grounded,grounded,Suicide,suicide,AnimTime,animTime) DEFINE_CREATE_FROM_STREAM(Seal) void Seal::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } Stats Seal::stats; DEFINE_CREATURE_CTORS_4(FireDemon,Flying,flying,BuiltIn,builtIn,OnFire,onFire,SwapProtect,swapProtect) DEFINE_CREATE_FROM_STREAM(FireDemon) Size FireDemon::get_shot_size(Dir) { return Fireball::get_size(); } PhysicalP FireDemon::create_shot(WorldP w,LocatorP l,const Pos &pos, const Id &shooter,Dir dir) { PhysicalP shot = new Fireball(w,l,pos,shooter,dir, FIRE_DEMON_FIREBALL_HEAT,FIRE_DEMON_FIREBALL_TIME, True); // Special fireballs that hurt those with the OnFire ability. assert(shot); return shot; } void FireDemon::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } DEFINE_CREATURE_CTORS_4(Dragon,Flying,flying,BuiltIn,builtIn,OnFire,onFire,Segmented,segmented) DEFINE_CREATE_FROM_STREAM(Dragon) Size Dragon::get_shot_size(Dir) { return Fireball::get_size(); } PhysicalP Dragon::create_shot(WorldP w,LocatorP l,const Pos &pos, const Id &shooter,Dir dir) { PhysicalP shot = new Fireball(w,l,pos,shooter,dir, DRAGON_FIREBALL_HEAT,DRAGON_FIREBALL_TIME, True); // Special fireballs that hurt those with the OnFire ability. assert(shot); return shot; } void Dragon::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } DEFINE_CREATURE_CTORS_4(Walker,User,user,Walking,walking,BuiltIn,builtIn,AnimTime,animTime) DEFINE_CREATE_FROM_STREAM(Walker) PhysicalP Walker::create_shot(WorldP w,LocatorP l,const Pos &pos, const Id &shooter,Dir dir) { PhysicalP shot = new Laser(w,l,pos,shooter,dir); assert(shot); return shot; } void Walker::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } Stats Walker::stats; DEFINE_CREATURE_CTORS_3(Dog,Carrier,carrier,Fighter,fighter,Walking,walking) DEFINE_CREATE_FROM_STREAM(Dog) Boolean Dog::draw_outline(ColorNum &) { // Dogs look stupid with an outline, the Dog is too small. return False; } void Dog::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } Stats Dog::stats; DEFINE_CREATURE_CTORS_3(Mutt,Carrier,carrier,Fighter,fighter,Walking,walking) DEFINE_CREATE_FROM_STREAM(Mutt) Boolean Mutt::draw_outline(ColorNum &) { // Dogs look stupid with an outline, the Dog is too small. return False; } void Mutt::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } Stats Mutt::stats; DEFINE_CREATURE_CTORS_4(Yeti,Prickly,prickly,Walking,walking, AnimTime,animTime,Sensitive,sensitive); DEFINE_CREATE_FROM_STREAM(Yeti) void Yeti::init_x(Xvars &xvars,IXCommand command,void* arg) { Moving::init_x(xvars,command,arg,creatureContext.movingContext,creatureXdata); } Stats Yeti::stats;