void CHSMTube::AttackObject(CHS3DObject * cSource, CHS3DObject * cTarget, CHSConsole * cConsole, HS_INT32 iSysType, HS_INT32 hit_flag) { HS_DBREF dbUser; // Grab the user of the console. dbUser = hsInterface.ConsoleUser(cConsole->m_objnum); if (cSource->GetType() == HST_SHIP) { CHSSysCloak *cCloak; CHSShip *ptr; ptr = (CHSShip *) cSource; // Look for the cloaking device. cCloak = (CHSSysCloak *) ptr->GetEngSystem(HSS_CLOAK); if (cCloak) if (cCloak->GetEngaged()) { if (dbUser != HSNOTHING) hsStdError(dbUser, "You cannot fire while cloaked."); return; } } // Can we attack that object? if (!CanAttackObject(cTarget)) { if (dbUser != HSNOTHING) hsStdError(dbUser, "You cannot attack that target with that weapon."); } // Create a missile object, and put it in space CHSMissile *cMiss; cMiss = new CHSMissile; cMiss->SetUID(cSource->GetUID()); if (hit_flag == 0) { cMiss->SetAutoMiss(); } // Add it to the universe CHSUniverse *uDest; uDest = cMiss->GetUniverse(); if (!uDest) { if (dbUser != HSNOTHING) { hsInterface.Notify(dbUser, "Error finding a universe to put the missile in."); } delete cMiss; return; } uDest->AddObject(cMiss); // Set missile coords cMiss->SetX(cSource->GetX()); cMiss->SetY(cSource->GetY()); cMiss->SetZ(cSource->GetZ()); // Set missile heading cMiss->SetHeading(cConsole->GetXYHeading(), cConsole->GetZHeading()); // Set missile type cMiss->SetWeaponData(this); // Set source info cMiss->SetSourceConsole(cConsole); cMiss->SetSourceObject(cSource); cMiss->SetTargetObject(cTarget); #ifdef PENNMUSH HS_DBREF obj_num = hsInterface.CreateNewGameObject(); #else HS_DBREF obj_num = hsInterface.CreateNewGameObject(TYPE_THING); #endif if (hsInterface.ValidObject(obj_num)) { cMiss->SetDbref(obj_num); // Try to set the name of the missile properly but default // just in case if (NULL == m_pWeaponData) { hsInterface.SetObjectName(obj_num, "Missile"); } else { hsInterface.SetObjectName(obj_num, static_cast < CHSMissileData * >(m_pWeaponData)->Name()); } hsInterface.MoveObject(obj_num, uDest->GetID()); hsInterface.SetToggle(obj_num, THING_HSPACE_OBJECT); hsInterface.SetObjectOwner(obj_num, hsInterface.GetGodDbref()); // Missile objects are temporary, clear them if the game // is restarted while the missile is still in existance hsInterface.AtrAdd(obj_num, "STARTUP", "@destroy me", hsInterface.GetGodDbref()); } else { // Set missile HS_DBREF to some very high number that's // probably not duplicated. cMiss->SetDbref(hsInterface.GetRandom(10000) + 28534); hs_log("CHSMTube::AttackObject() -- Deprecated fake dbref method \ utilized."); } if (dbUser != HSNOTHING) { hsStdError(dbUser, "Missile launched."); } m_loaded = false; }
// Attacks a target object. void CHSLaser::AttackObject(CHSObject *cSource, CHSObject *cTarget, CHSConsole *cConsole, int iSysType) { dbref dbUser; int iAttackRoll; int iDefendRoll; int i; CHSObject *cCTarget; double sX, sY, sZ; // Source object coords double tX, tY, tZ; // Target object coords; // Grab the user of the console. dbUser = hsInterface.ConsoleUser(cConsole->m_objnum); // Can we attack that object? if (cSource->GetType() == HST_SHIP) { CHSSysCloak *cCloak; CHSShip *ptr; float rval; ptr = (CHSShip *)cSource; // Look for the cloaking device. cCloak = (CHSSysCloak *)ptr->GetEngSystem(HSS_CLOAK); if (cCloak) if (cCloak->GetEngaged()) { if (dbUser != NOTHING) hsStdError(dbUser, "You cannot fire while cloaked."); return; } } if (!CanAttackObject(cTarget)) { if (dbUser != NOTHING) hsStdError(dbUser, "You cannot attack that target with that weapon."); } // Calculate distance to object sX = cSource->GetX(); sY = cSource->GetY(); sZ = cSource->GetZ(); tX = cTarget->GetX(); tY = cTarget->GetY(); tZ = cTarget->GetZ(); double dDistance; dDistance = Dist3D(sX, sY, sZ, tX, tY, tZ) + .00001; // Size of a target ship matters relative to distance. // The closer a target gets to the ship, the larger // it effectively is. That is to say it takes up more // of the view angle. When the target is right next // to the ship, in front of the gun, it is essentially // the broad side of a barn, which everyone can hit. // Thus, to handle this we'll calculate the size of // the target and the viewing angle it takes up. double dSize; // Size of the side of the target double dAngle; // Amount of viewing angle taken up by size dSize = cTarget->GetSize(); dSize = (.7 * dSize) * (.7 * dSize); // Halve the size, and divide by distance. This // gives us the tangent of the angle taken up by // the target. dSize = (dSize * .5) / dDistance; // Take the inverse tangent to get angle. dAngle = atan(dSize); // Double the angle because we used half of the size // to get the angle of a right triangle. dAngle *= 2; // We now have the viewing angle consumed by the // target. There's a maximum possible value of 180, // so divide by that to determine how much of the viewing // angle is taken up by the target. dSize = dAngle * .005555; // Subtract from 1 to get maximum values of 1 when the // angle is small. dSize = 1 - dSize; // Now multiply by 6 to get relative difficulty of hitting // target. iDefendRoll = (int) (6 * dSize) + getrandom(6); iAttackRoll = GetAccuracy() + getrandom(6); // Simulate difficulty when a target is moving. // If the target is moving toward or away from the // attacker, it's not very difficult. Thus, we // calculate the change in angle for the target // during one cycle. The maximum change is 180 // degrees. CHSVector tVec; CHSVector aVec; tVec = cTarget->GetMotionVector(); aVec = cSource->GetMotionVector(); // Calculate vector to target now. double dx, dy, dz; dx = tX - sX; dy = tY - sY; dz = tZ - sZ; // Make a unit vector dx /= dDistance; dy /= dDistance; dz /= dDistance; CHSVector nowVec(dx, dy, dz); // Now calculate coordinate for source and target // in one cycle. double sX2, sY2, sZ2; double tX2, tY2, tZ2; double aSpeed, tSpeed; // Grab both object speeds, and bring them down // to per-second levels. aSpeed = cSource->GetSpeed() * .0002778; tSpeed = cTarget->GetSpeed() * .0002778; // Calculate coordinates for next cycle. sX2 = sX + (aVec.i() * aSpeed); sY2 = sY + (aVec.j() * aSpeed); sZ2 = sZ + (aVec.k() * aSpeed); tX2 = tX + (tVec.i() * tSpeed); tY2 = tY + (tVec.j() * tSpeed); tZ2 = tZ + (tVec.k() * tSpeed); // Calculate vector to target after next cycle dx = tX2 - sX2; dy = tY2 - sY2; dz = tZ2 - sZ2; // Divide by distance to make a unit vector double dDistance2; dDistance2 = Dist3D(sX2, sY2, sZ2, tX2, tY2, tZ2); dx /= dDistance2; dy /= dDistance2; dz /= dDistance2; CHSVector nextVec(dx, dy, dz); // Calculate the dot product between the previous // and the next cycle vectors. double dp; dp = nowVec.DotProduct(nextVec); // Calculate the angle change. This is in radians. dAngle = acos(dp); // Now divide angle change by 2pi to get change in angle // from 0 to 1, where 1 is a huge change in angle and, // therefore, high difficulty. dAngle *= .15915; // Add up to 6 points of defense for "evasion" by angle // change. iDefendRoll += (int) (6 * dAngle); // If distance is farther than our range, the shot always // misses. double range; range = GetRange(); CHSUniverse *uDest; char tbuf[256]; char fstat1[128]; char fstat2[128]; if (dDistance >= range || iDefendRoll > iAttackRoll) { sprintf(fstat1, "%s%smisses%s",ANSI_HILITE,ANSI_GREEN,ANSI_NORMAL); sprintf(fstat2, "%s%smissed%s",ANSI_HILITE,ANSI_GREEN,ANSI_NORMAL); } else { sprintf(fstat1, "%s%shits%s",ANSI_HILITE,ANSI_RED,ANSI_NORMAL); sprintf(fstat2, "%s%shit%s",ANSI_HILITE,ANSI_RED,ANSI_NORMAL); } uDest = uaUniverses.FindUniverse(cSource->GetUID()); CHSSysSensors *cSensors; SENSOR_CONTACT *cContactS; SENSOR_CONTACT *cContactD; for (i = 0; i < HS_MAX_ACTIVE_OBJECTS; i++) { cCTarget = uDest->GetActiveUnivObject(i); if (!cCTarget) continue; if (cCTarget == cSource || cCTarget == cTarget) continue; cSensors = (CHSSysSensors *)cCTarget->GetEngSystem(HSS_SENSORS); if (!cSensors) continue; cContactS = cSensors->GetContact(cSource); cContactD = cSensors->GetContact(cTarget); if (!cContactS && !cContactD) continue; if (!cContactS && cContactD) { if (cContactD->status == DETECTED) { sprintf(tbuf, "%s[%s%s%d%s%s]%s - Unknown contact is being fired upon and %s",cTarget->GetObjectColor(),ANSI_NORMAL,ANSI_HILITE,cContactD->m_id,ANSI_NORMAL,cTarget->GetObjectColor(),ANSI_NORMAL, fstat2); cCTarget->HandleMessage(tbuf, MSG_SENSOR, (long *)cCTarget); } else if (cContactD->status == IDENTIFIED) { sprintf(tbuf, "%s[%s%s%d%s%s]%s - The %s is being fired upon and %s",cTarget->GetObjectColor(),ANSI_NORMAL,ANSI_HILITE,cContactD->m_id,ANSI_NORMAL,cTarget->GetObjectColor(),ANSI_NORMAL, cSource->GetName(), fstat2); cCTarget->HandleMessage(tbuf, MSG_SENSOR, (long *)cCTarget); } continue; } if (cContactS && !cContactD) { if (cContactS->status == DETECTED) { sprintf(tbuf, "%s[%s%s%d%s%s]%s - Unknown contact is firing upon something",cSource->GetObjectColor(),ANSI_NORMAL,ANSI_HILITE,cContactS->m_id,ANSI_NORMAL,cSource->GetObjectColor(),ANSI_NORMAL); cCTarget->HandleMessage(tbuf, MSG_SENSOR, (long *)cCTarget); } else if (cContactS->status == IDENTIFIED) { sprintf(tbuf, "%s[%s%s%d%s%s]%s - The %s is firing upon something",cSource->GetObjectColor(),ANSI_NORMAL,ANSI_HILITE,cContactS->m_id,ANSI_NORMAL,cSource->GetObjectColor(),ANSI_NORMAL, cSource->GetName()); cCTarget->HandleMessage(tbuf, MSG_SENSOR, (long *)cCTarget); } continue; } if (cContactS && cContactD) if (cContactS->status == DETECTED && cContactD->status == DETECTED) { sprintf(tbuf, "%s[%s%s%d%s%s]%s - Unknown contact fires and %s unknown contact %s[%s%s%d%s%s]%s",cSource->GetObjectColor(),ANSI_NORMAL,ANSI_HILITE,cContactS->m_id,ANSI_NORMAL,cSource->GetObjectColor(),ANSI_NORMAL, fstat1,cTarget->GetObjectColor(),ANSI_NORMAL,ANSI_HILITE,cContactD->m_id,ANSI_NORMAL,cTarget->GetObjectColor(),ANSI_NORMAL); cCTarget->HandleMessage(tbuf,MSG_SENSOR, (long *)cCTarget); } else if (cContactS->status == IDENTIFIED && cContactD->status == IDENTIFIED) { sprintf(tbuf, "%s[%s%s%d%s%s]%s - The %s fires and %s the %s",cSource->GetObjectColor(),ANSI_NORMAL,ANSI_HILITE,cContactS->m_id,ANSI_NORMAL,cSource->GetObjectColor(),ANSI_NORMAL, cSource->GetName(), fstat1, cTarget->GetName()); cCTarget->HandleMessage(tbuf, MSG_SENSOR, (long *)cCTarget); } else if (cContactS->status == IDENTIFIED && cContactD->status == DETECTED) { sprintf(tbuf, "%s[%s%s%d%s%s]%s - The %s fires and %s unknown contact %s[%s%s%d%s%s]%s",cSource->GetObjectColor(),ANSI_NORMAL,ANSI_HILITE,cContactS->m_id,ANSI_NORMAL,cSource->GetObjectColor(),ANSI_NORMAL, cSource->GetName(), fstat1,cTarget->GetObjectColor(),ANSI_NORMAL,ANSI_HILITE,cContactD->m_id,ANSI_NORMAL,cTarget->GetObjectColor(),ANSI_NORMAL); cCTarget->HandleMessage(tbuf, MSG_SENSOR, (long *)cCTarget); } else if (cContactS->status == DETECTED && cContactD->status == IDENTIFIED) { sprintf(tbuf, "%s[%s%s%d%s%s]%s - Unknown contact fires and %s the %s",cSource->GetObjectColor(),ANSI_NORMAL,ANSI_HILITE,cContactS->m_id,ANSI_NORMAL,cSource->GetObjectColor(),ANSI_NORMAL, fstat1, cTarget->GetName()); cCTarget->HandleMessage(tbuf, MSG_SENSOR, (long *)cCTarget); } } if (dDistance >= range) { if (dbUser != NOTHING) hsStdError( dbUser, "Your shot dissipates short of its target."); strcpy(tbuf, "An incoming energy shot has missed us."); cTarget->HandleMessage(tbuf, MSG_COMBAT, (long *)cSource); } else if (iAttackRoll > iDefendRoll) { // The weapon hits! // Determine strength based on base weapon // strength and range to target. int strength; strength = GetStrength(); if (dDistance > (range * .333)) { strength = (int)(strength * (.333 + (1 - (dDistance / (range + .0001))))); } // If iSysType is not HSS_NOTYPE, then do a roll // against the accuracy of the weapon to see if // the system gets hit. if (iSysType != HSS_NOTYPE) { UINT ARoll, SRoll; ARoll = getrandom(GetAccuracy()); SRoll = getrandom(10); if (SRoll > ARoll) iSysType = HSS_NOTYPE; // Didn't succeed } // Tell the target to take damage cTarget->HandleDamage(cSource, this, strength, cConsole, iSysType); } else { // The weapon misses. :( if (dbUser != NOTHING) hsStdError(dbUser, "Your shot skims past your target and out into space."); strcpy(tbuf, "An incoming energy shot has missed us."); cTarget->HandleMessage(tbuf, MSG_COMBAT, (long *)cSource); } Regenerate(); }