void sector_t::ClosestPoint(fixed_t fx, fixed_t fy, fixed_t &ox, fixed_t &oy) const { int i; double x = fx, y = fy; double bestdist = HUGE_VAL; double bestx = 0, besty = 0; for (i = 0; i < linecount; ++i) { vertex_t *v1 = lines[i]->v1; vertex_t *v2 = lines[i]->v2; double a = v2->x - v1->x; double b = v2->y - v1->y; double den = a*a + b*b; double ix, iy, dist; if (den == 0) { // Line is actually a point! ix = v1->x; iy = v1->y; } else { double num = (x - v1->x) * a + (y - v1->y) * b; double u = num / den; if (u <= 0) { ix = v1->x; iy = v1->y; } else if (u >= 1) { ix = v2->x; iy = v2->y; } else { ix = v1->x + u * a; iy = v1->y + u * b; } } a = (ix - x); b = (iy - y); dist = a*a + b*b; if (dist < bestdist) { bestdist = dist; bestx = ix; besty = iy; } } ox = fixed_t(bestx); oy = fixed_t(besty); }
static void CalcPlane (SlopeWork &slope, secplane_t &plane) { FVector3 pt[3]; long j; slope.x[0] = slope.wal->x; slope.y[0] = slope.wal->y; slope.x[1] = slope.wal2->x; slope.y[1] = slope.wal2->y; if (slope.dx == 0) { slope.x[2] = slope.x[0] + 64; slope.y[2] = slope.y[0]; } else { slope.x[2] = slope.x[0]; slope.y[2] = slope.y[0] + 64; } j = DMulScale3 (slope.dx, slope.y[2]-slope.wal->y, -slope.dy, slope.x[2]-slope.wal->x); slope.z[2] += Scale (slope.heinum, j, slope.i); pt[0] = FVector3(slope.dx, -slope.dy, 0); pt[1] = FVector3(slope.x[2] - slope.x[0], slope.y[0] - slope.y[2], (slope.z[2] - slope.z[0]) / 16); pt[2] = (pt[0] ^ pt[1]).Unit(); if ((pt[2][2] < 0 && plane.c > 0) || (pt[2][2] > 0 && plane.c < 0)) { pt[2] = -pt[2]; } plane.a = fixed_t(pt[2][0]*65536.f); plane.b = fixed_t(pt[2][1]*65536.f); plane.c = fixed_t(pt[2][2]*65536.f); plane.ic = fixed_t(65536.f/pt[2][2]); plane.d = -TMulScale8 (plane.a, slope.x[0]<<4, plane.b, (-slope.y[0])<<4, plane.c, slope.z[0]); }
int FNodeBuilder::CloseSubsector (TArray<seg_t> &segs, int subsector, vertex_t *outVerts) { FPrivSeg *seg, *prev; angle_t prevAngle; double accumx, accumy; fixed_t midx, midy; int firstVert; DWORD first, max, count, i, j; first = Subsectors[subsector].firstline; max = first + Subsectors[subsector].numlines; count = 0; accumx = accumy = 0.0; for (i = first; i < max; ++i) { seg = &Segs[SegList[i].SegNum]; accumx += double(Vertices[seg->v1].x) + double(Vertices[seg->v2].x); accumy += double(Vertices[seg->v1].y) + double(Vertices[seg->v2].y); } midx = fixed_t(accumx / (max - first) / 2); midy = fixed_t(accumy / (max - first) / 2); seg = &Segs[SegList[first].SegNum]; prevAngle = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); seg->storedseg = PushGLSeg (segs, seg, outVerts); count = 1; prev = seg; firstVert = seg->v1; for (i = first + 1; i < max; ++i) { angle_t bestdiff = ANGLE_MAX; FPrivSeg *bestseg = NULL; DWORD bestj = DWORD_MAX; j = first; do { seg = &Segs[SegList[j].SegNum]; angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); angle_t diff = prevAngle - ang; if (seg->v1 == prev->v2) { bestdiff = diff; bestseg = seg; bestj = j; break; } if (diff < bestdiff && diff > 0) { bestdiff = diff; bestseg = seg; bestj = j; } } while (++j < max); // Is a NULL bestseg actually okay? if (bestseg != NULL) { seg = bestseg; } if (prev->v2 != seg->v1) { // Add a new miniseg to connect the two segs PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[seg->v1]); count++; } prevAngle -= bestdiff; seg->storedseg = PushGLSeg (segs, seg, outVerts); count++; prev = seg; if (seg->v2 == firstVert) { prev = seg; break; } } if (prev->v2 != firstVert) { PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[firstVert]); count++; } return count; }
int FNodeBuilder::CloseSubsector (TArray<glseg_t> &segs, int subsector, vertex_t *outVerts) { FPrivSeg *seg, *prev; angle_t prevAngle; double accumx, accumy; fixed_t midx, midy; int firstVert; uint32_t first, max, count, i, j; bool diffplanes; int firstplane; first = (uint32_t)(size_t)Subsectors[subsector].firstline; max = first + Subsectors[subsector].numlines; count = 0; accumx = accumy = 0.0; diffplanes = false; firstplane = Segs[SegList[first].SegNum].planenum; // Calculate the midpoint of the subsector and also check for degenerate subsectors. // A subsector is degenerate if it exists in only one dimension, which can be // detected when all the segs lie in the same plane. This can happen if you have // outward-facing lines in the void that don't point toward any sector. (Some of the // polyobjects in Hexen are constructed like this.) for (i = first; i < max; ++i) { seg = &Segs[SegList[i].SegNum]; accumx += double(Vertices[seg->v1].x) + double(Vertices[seg->v2].x); accumy += double(Vertices[seg->v1].y) + double(Vertices[seg->v2].y); if (firstplane != seg->planenum) { diffplanes = true; } } midx = fixed_t(accumx / (max - first) / 2); midy = fixed_t(accumy / (max - first) / 2); seg = &Segs[SegList[first].SegNum]; prevAngle = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); seg->storedseg = PushGLSeg (segs, seg, outVerts); count = 1; prev = seg; firstVert = seg->v1; #ifdef DD Printf(PRINT_LOG, "--%d--\n", subsector); for (j = first; j < max; ++j) { seg = &Segs[SegList[j].SegNum]; angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); Printf(PRINT_LOG, "%d%c %5d(%5d,%5d)->%5d(%5d,%5d) - %3.5f %d,%d [%08x,%08x]-[%08x,%08x]\n", j, seg->linedef == -1 ? '+' : ':', seg->v1, Vertices[seg->v1].x>>16, Vertices[seg->v1].y>>16, seg->v2, Vertices[seg->v2].x>>16, Vertices[seg->v2].y>>16, double(ang/2)*180/(1<<30), seg->planenum, seg->planefront, Vertices[seg->v1].x, Vertices[seg->v1].y, Vertices[seg->v2].x, Vertices[seg->v2].y); } #endif if (diffplanes) { // A well-behaved subsector. Output the segs sorted by the angle formed by connecting // the subsector's center to their first vertex. D(Printf(PRINT_LOG, "Well behaved subsector\n")); for (i = first + 1; i < max; ++i) { angle_t bestdiff = ANGLE_MAX; FPrivSeg *bestseg = NULL; uint32_t bestj = DWORD_MAX; j = first; do { seg = &Segs[SegList[j].SegNum]; angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); angle_t diff = prevAngle - ang; if (seg->v1 == prev->v2) { bestdiff = diff; bestseg = seg; bestj = j; break; } if (diff < bestdiff && diff > 0) { bestdiff = diff; bestseg = seg; bestj = j; } } while (++j < max); // Is a NULL bestseg actually okay? if (bestseg != NULL) { seg = bestseg; } if (prev->v2 != seg->v1) { // Add a new miniseg to connect the two segs PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[seg->v1]); count++; } #ifdef DD Printf(PRINT_LOG, "+%d\n", bestj); #endif prevAngle -= bestdiff; seg->storedseg = PushGLSeg (segs, seg, outVerts); count++; prev = seg; if (seg->v2 == firstVert) { prev = seg; break; } } #ifdef DD Printf(PRINT_LOG, "\n"); #endif } else { // A degenerate subsector. These are handled in three stages: // Stage 1. Proceed in the same direction as the start seg until we // hit the seg furthest from it. // Stage 2. Reverse direction and proceed until we hit the seg // furthest from the start seg. // Stage 3. Reverse direction again and insert segs until we get // to the start seg. // A dot product serves to determine distance from the start seg. D(Printf(PRINT_LOG, "degenerate subsector\n")); // Stage 1. Go forward. count += OutputDegenerateSubsector (segs, subsector, true, 0, prev, outVerts); // Stage 2. Go backward. count += OutputDegenerateSubsector (segs, subsector, false, DBL_MAX, prev, outVerts); // Stage 3. Go forward again. count += OutputDegenerateSubsector (segs, subsector, true, -DBL_MAX, prev, outVerts); } if (prev->v2 != firstVert) { PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[firstVert]); count++; } #ifdef DD Printf(PRINT_LOG, "Output GL subsector %d:\n", subsector); for (i = segs.Size() - count; i < (int)segs.Size(); ++i) { Printf(PRINT_LOG, " Seg %5d%c(%5d,%5d)-(%5d,%5d) [%08x,%08x]-[%08x,%08x]\n", i, segs[i].linedef == NULL ? '+' : ' ', segs[i].v1->fixX()>>16, segs[i].v1->fixY()>>16, segs[i].v2->fixX()>>16, segs[i].v2->fixY()>>16, segs[i].v1->fixX(), segs[i].v1->fixY(), segs[i].v2->fixX(), segs[i].v2->fixY()); } #endif return count; }
//========================================================================== // // ParseStates // parses a state block // //========================================================================== int ParseStates(FActorInfo * actor, AActor * defaults, Baggage &bag) { FString statestring; intptr_t count = 0; FState state; FState * laststate = NULL; intptr_t lastlabel = -1; int minrequiredstate = -1; SC_MustGetStringName ("{"); SC_SetEscape(false); // disable escape sequences in the state parser while (!SC_CheckString ("}") && !sc_End) { memset(&state,0,sizeof(state)); statestring = ParseStateString(); if (!statestring.CompareNoCase("GOTO")) { do_goto: statestring = ParseStateString(); if (SC_CheckString ("+")) { SC_MustGetNumber (); statestring += '+'; statestring += sc_String; } // copy the text - this must be resolved later! if (laststate != NULL) { // Following a state definition: Modify it. laststate->NextState = (FState*)copystring(statestring); } else if (lastlabel >= 0) { // Following a label: Retarget it. RetargetStates (count+1, statestring); } else { SC_ScriptError("GOTO before first state"); } } else if (!statestring.CompareNoCase("STOP")) { do_stop: if (laststate!=NULL) { laststate->NextState=(FState*)-1; } else if (lastlabel >=0) { RetargetStates (count+1, NULL); } else { SC_ScriptError("STOP before first state"); continue; } } else if (!statestring.CompareNoCase("WAIT") || !statestring.CompareNoCase("FAIL")) { if (!laststate) { SC_ScriptError("%s before first state", sc_String); continue; } laststate->NextState=(FState*)-2; } else if (!statestring.CompareNoCase("LOOP")) { if (!laststate) { SC_ScriptError("LOOP before first state"); continue; } laststate->NextState=(FState*)(lastlabel+1); } else { const char * statestrp; SC_MustGetString(); if (SC_Compare (":")) { laststate = NULL; do { lastlabel = count; AddState(statestring, (FState *) (count+1)); statestring = ParseStateString(); if (!statestring.CompareNoCase("GOTO")) { goto do_goto; } else if (!statestring.CompareNoCase("STOP")) { goto do_stop; } SC_MustGetString (); } while (SC_Compare (":")); // continue; } SC_UnGet (); if (statestring.Len() != 4) { SC_ScriptError ("Sprite names must be exactly 4 characters\n"); } memcpy(state.sprite.name, statestring, 4); state.Misc1=state.Misc2=0; state.ParameterIndex=0; SC_MustGetString(); statestring = (sc_String+1); statestrp = statestring; state.Frame=(*sc_String&223)-'A'; if ((*sc_String&223)<'A' || (*sc_String&223)>']') { SC_ScriptError ("Frames must be A-Z, [, \\, or ]"); state.Frame=0; } SC_MustGetNumber(); sc_Number++; state.Tics=sc_Number&255; state.Misc1=(sc_Number>>8)&255; if (state.Misc1) state.Frame|=SF_BIGTIC; while (SC_GetString() && !sc_Crossed) { if (SC_Compare("BRIGHT")) { state.Frame|=SF_FULLBRIGHT; continue; } if (SC_Compare("OFFSET")) { if (state.Frame&SF_BIGTIC) { SC_ScriptError("You cannot use OFFSET with a state duration larger than 254!"); } // specify a weapon offset SC_MustGetStringName("("); SC_MustGetNumber(); state.Misc1=sc_Number; SC_MustGetStringName (","); SC_MustGetNumber(); state.Misc2=sc_Number; SC_MustGetStringName(")"); continue; } // Make the action name lowercase to satisfy the gperf hashers strlwr (sc_String); int minreq=count; if (DoActionSpecials(state, !statestring.IsEmpty(), &minreq, bag)) { if (minreq>minrequiredstate) minrequiredstate=minreq; goto endofstate; } PSymbol *sym = bag.Info->Class->Symbols.FindSymbol (FName(sc_String, true), true); if (sym != NULL && sym->SymbolType == SYM_ActionFunction) { PSymbolActionFunction *afd = static_cast<PSymbolActionFunction *>(sym); state.Action = afd->Function; if (!afd->Arguments.IsEmpty()) { const char *params = afd->Arguments.GetChars(); int numparams = (int)afd->Arguments.Len(); int v; if (!islower(*params)) { SC_MustGetStringName("("); } else { if (!SC_CheckString("(")) goto endofstate; } int paramindex = PrepareStateParameters(&state, numparams); int paramstart = paramindex; bool varargs = params[numparams - 1] == '+'; if (varargs) { StateParameters[paramindex++] = 0; } while (*params) { switch(*params) { case 'I': case 'i': // Integer SC_MustGetNumber(); v=sc_Number; break; case 'F': case 'f': // Fixed point SC_MustGetFloat(); v=fixed_t(sc_Float*FRACUNIT); break; case 'S': case 's': // Sound name SC_MustGetString(); v=S_FindSound(sc_String); break; case 'M': case 'm': // Actor name case 'T': case 't': // String SC_SetEscape(true); SC_MustGetString(); SC_SetEscape(false); v = (int)(sc_String[0] ? FName(sc_String) : NAME_None); break; case 'L': case 'l': // Jump label if (SC_CheckNumber()) { if (strlen(statestring)>0) { SC_ScriptError("You cannot use A_Jump commands with a jump index on multistate definitions\n"); } v=sc_Number; if (v<1) { SC_ScriptError("Negative jump offsets are not allowed"); } { int minreq=count+v; if (minreq>minrequiredstate) minrequiredstate=minreq; } } else { if (JumpParameters.Size()==0) JumpParameters.Push(NAME_None); v = -(int)JumpParameters.Size(); // This forces quotation marks around the state name. SC_MustGetToken(TK_StringConst); FString statestring = sc_String; // ParseStateString(); const PClass *stype=NULL; int scope = statestring.IndexOf("::"); if (scope >= 0) { FName scopename = FName(statestring, scope, false); if (scopename == NAME_Super) { // Super refers to the direct superclass scopename = actor->Class->ParentClass->TypeName; } JumpParameters.Push(scopename); statestring = statestring.Right(statestring.Len()-scope-2); stype = PClass::FindClass (scopename); if (stype == NULL) { SC_ScriptError ("%s is an unknown class.", scopename.GetChars()); } if (!stype->IsDescendantOf (RUNTIME_CLASS(AActor))) { SC_ScriptError ("%s is not an actor class, so it has no states.", stype->TypeName.GetChars()); } if (!stype->IsAncestorOf (actor->Class)) { SC_ScriptError ("%s is not derived from %s so cannot access its states.", actor->Class->TypeName.GetChars(), stype->TypeName.GetChars()); } } else { // No class name is stored. This allows 'virtual' jumps to // labels in subclasses. // It also means that the validity of the given state cannot // be checked here. JumpParameters.Push(NAME_None); } TArray<FName> names; MakeStateNameList(statestring, &names); if (stype != NULL) { if (!stype->ActorInfo->FindState(names.Size(), &names[0])) { SC_ScriptError("Jump to unknown state '%s' in class '%s'", statestring.GetChars(), stype->TypeName.GetChars()); } } JumpParameters.Push((ENamedName)names.Size()); for(unsigned i=0;i<names.Size();i++) { JumpParameters.Push(names[i]); } // No offsets here. The point of jumping to labels is to avoid such things! } break; case 'C': case 'c': // Color SC_MustGetString (); if (SC_Compare("none")) { v = -1; } else { int c = V_GetColor (NULL, sc_String); // 0 needs to be the default so we have to mark the color. v = MAKEARGB(1, RPART(c), GPART(c), BPART(c)); } break; case 'X': case 'x': v = ParseExpression (false, bag.Info->Class); break; case 'Y': case 'y': v = ParseExpression (true, bag.Info->Class); break; default: assert(false); v = -1; break; } StateParameters[paramindex++] = v; params++; if (varargs) { StateParameters[paramstart]++; } if (*params) { if (*params == '+') { if (SC_CheckString(")")) { goto endofstate; } params--; v = 0; StateParameters.Push(v); } else if ((islower(*params) || *params=='!') && SC_CheckString(")")) { goto endofstate; } SC_MustGetStringName (","); } } SC_MustGetStringName(")"); } else { SC_MustGetString(); if (SC_Compare("(")) { SC_ScriptError("You cannot pass parameters to '%s'\n",sc_String); } SC_UnGet(); } goto endofstate; } SC_ScriptError("Invalid state parameter %s\n", sc_String); } SC_UnGet(); endofstate: StateArray.Push(state); while (*statestrp) { int frame=((*statestrp++)&223)-'A'; if (frame<0 || frame>28) { SC_ScriptError ("Frames must be A-Z, [, \\, or ]"); frame=0; } state.Frame=(state.Frame&(SF_FULLBRIGHT|SF_BIGTIC))|frame; StateArray.Push(state); count++; } laststate=&StateArray[count]; count++; } } if (count<=minrequiredstate) { SC_ScriptError("A_Jump offset out of range in %s", actor->Class->TypeName.GetChars()); } SC_SetEscape(true); // re-enable escape sequences return count; }
bool P_Thing_Projectile (int tid, AActor *source, int type, const char *type_name, angle_t angle, fixed_t speed, fixed_t vspeed, int dest, AActor *forcedest, int gravity, int newtid, bool leadTarget) { int rtn = 0; const PClass *kind; AActor *spot, *mobj, *targ = forcedest; FActorIterator iterator (tid); double fspeed = speed; int defflags3; if (type_name == NULL) { if (type >= MAX_SPAWNABLES) return false; if ((kind = SpawnableThings[type]) == NULL) return false; } else { if ((kind = PClass::FindClass(type_name)) == NULL || kind->ActorInfo == NULL) return false; } // Handle decorate replacements. kind = kind->ActorInfo->GetReplacement()->Class; defflags3 = GetDefaultByType (kind)->flags3; if ((defflags3 & MF3_ISMONSTER) && ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) return false; if (tid == 0) { spot = source; } else { spot = iterator.Next(); } while (spot != NULL) { FActorIterator tit (dest); if (dest == 0 || (targ = tit.Next())) { do { fixed_t z = spot->z; if (defflags3 & MF3_FLOORHUGGER) { z = ONFLOORZ; } else if (defflags3 & MF3_CEILINGHUGGER) { z = ONCEILINGZ; } else if (z != ONFLOORZ) { z -= spot->floorclip; } mobj = Spawn (kind, spot->x, spot->y, z, ALLOW_REPLACE); if (mobj) { mobj->tid = newtid; mobj->AddToHash (); P_PlaySpawnSound(mobj, spot); if (gravity) { mobj->flags &= ~MF_NOGRAVITY; if (!(mobj->flags3 & MF3_ISMONSTER) && gravity == 1) { mobj->gravity = FRACUNIT/8; } } else { mobj->flags |= MF_NOGRAVITY; } mobj->target = spot; if (targ != NULL) { fixed_t spot[3] = { targ->x, targ->y, targ->z+targ->height/2 }; FVector3 aim(float(spot[0] - mobj->x), float(spot[1] - mobj->y), float(spot[2] - mobj->z)); if (leadTarget && speed > 0 && (targ->velx | targ->vely | targ->velz)) { // Aiming at the target's position some time in the future // is basically just an application of the law of sines: // a/sin(A) = b/sin(B) // Thanks to all those on the notgod phorum for helping me // with the math. I don't think I would have thought of using // trig alone had I been left to solve it by myself. FVector3 tvel(targ->velx, targ->vely, targ->velz); if (!(targ->flags & MF_NOGRAVITY) && targ->waterlevel < 3) { // If the target is subject to gravity and not underwater, // assume that it isn't moving vertically. Thanks to gravity, // even if we did consider the vertical component of the target's // velocity, we would still miss more often than not. tvel.Z = 0.0; if ((targ->velx | targ->vely) == 0) { goto nolead; } } double dist = aim.Length(); double targspeed = tvel.Length(); double ydotx = -aim | tvel; double a = acos (clamp (ydotx / targspeed / dist, -1.0, 1.0)); double multiplier = double(pr_leadtarget.Random2())*0.1/255+1.1; double sinb = -clamp (targspeed*multiplier * sin(a) / fspeed, -1.0, 1.0); // Use the cross product of two of the triangle's sides to get a // rotation vector. FVector3 rv(tvel ^ aim); // The vector must be normalized. rv.MakeUnit(); // Now combine the rotation vector with angle b to get a rotation matrix. FMatrix3x3 rm(rv, cos(asin(sinb)), sinb); // And multiply the original aim vector with the matrix to get a // new aim vector that leads the target. FVector3 aimvec = rm * aim; // And make the projectile follow that vector at the desired speed. double aimscale = fspeed / dist; mobj->velx = fixed_t (aimvec[0] * aimscale); mobj->vely = fixed_t (aimvec[1] * aimscale); mobj->velz = fixed_t (aimvec[2] * aimscale); mobj->angle = R_PointToAngle2 (0, 0, mobj->velx, mobj->vely); } else { nolead: mobj->angle = R_PointToAngle2 (mobj->x, mobj->y, targ->x, targ->y); aim.Resize (fspeed); mobj->velx = fixed_t(aim[0]); mobj->vely = fixed_t(aim[1]); mobj->velz = fixed_t(aim[2]); } if (mobj->flags2 & MF2_SEEKERMISSILE) { mobj->tracer = targ; } } else { mobj->angle = angle; mobj->velx = FixedMul (speed, finecosine[angle>>ANGLETOFINESHIFT]); mobj->vely = FixedMul (speed, finesine[angle>>ANGLETOFINESHIFT]); mobj->velz = vspeed; } // Set the missile's speed to reflect the speed it was spawned at. if (mobj->flags & MF_MISSILE) { mobj->Speed = fixed_t (sqrt (double(mobj->velx)*mobj->velx + double(mobj->vely)*mobj->vely + double(mobj->velz)*mobj->velz)); } // Hugger missiles don't have any vertical velocity if (mobj->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)) { mobj->velz = 0; } if (mobj->flags & MF_SPECIAL) { mobj->flags |= MF_DROPPED; } if (mobj->flags & MF_MISSILE) { if (P_CheckMissileSpawn (mobj)) { rtn = true; } } else if (!P_TestMobjLocation (mobj)) { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. if (mobj->CountsAsKill()) { level.total_monsters--; } // Same, for items if (mobj->flags & MF_COUNTITEM) { level.total_items--; } mobj->Destroy (); } else { // It spawned fine. rtn = 1; } } } while (dest != 0 && (targ = tit.Next())); } spot = iterator.Next(); } return rtn != 0; }
bool P_Thing_ProjectileEx (int tid, AActor *source, int type, const char *type_name, angle_t angle, fixed_t spawnx, fixed_t spawny, fixed_t spawnz, fixed_t speed, fixed_t vspeed, int dest, AActor *forcedest, int gravity, int newtid, bool leadTarget) { int rtn = 0; const PClass *kind; AActor *spot, *mobj, *targ = forcedest; FActorIterator iterator (tid); double fspeed = speed; int defflags3; fixed_t x = 0; fixed_t y = 0; fixed_t z = 0; angle_t pitch; if (type_name == NULL) { kind = P_GetSpawnableType(type); } else { kind = PClass::FindClass(type_name); } if (kind == NULL || kind->ActorInfo == NULL) { return false; } /* // [RC] I'm sure there was reason for this code here, but I'm not sure why anymore. Possibly // deprecated ThingProjectile code. if (type_name == NULL) { if (type >= MAX_SPAWNABLES) return false; if ((kind = SpawnableThings[type]) == NULL) return false; } else { if ((kind = PClass::FindClass(type_name)) == NULL || kind->ActorInfo == NULL) return false; } */ // Handle decorate replacements. kind = kind->GetReplacement(); defflags3 = GetDefaultByType (kind)->flags3; if ((defflags3 & MF3_ISMONSTER) && ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) return false; if (tid == 0) { spot = source; } else { spot = iterator.Next(); } while (spot != NULL) { FActorIterator tit (dest); if (dest == 0 || (targ = tit.Next())) { do { //fixed_t z = spot->z; z += spot->player->mo->z + (spot->player->mo->AttackZOffset+spawnz);//spot->player->mo->AttackZOffset; x += spot->player->mo->x + spawnx; y += spot->player->mo->y + spawny; pitch = spot->player->mo->pitch; if (defflags3 & MF3_FLOORHUGGER) z = ONFLOORZ; else if (defflags3 & MF3_CEILINGHUGGER) z = ONCEILINGZ; else if (z != ONFLOORZ)z -= spot->floorclip; if (z < spot->floorz) z = spot->floorz; mobj = Spawn (kind, x, y, z, ALLOW_REPLACE); if (mobj) { mobj->tid = newtid; mobj->AddToHash (); P_PlaySpawnSound(mobj, spot); if (gravity) { mobj->flags &= ~MF_NOGRAVITY; if (!(mobj->flags3 & MF3_ISMONSTER) && gravity == 1) { mobj->gravity = FRACUNIT/8; } } else { mobj->flags |= MF_NOGRAVITY; } mobj->target = spot; mobj->master = spot; //Ideally, give the projectile an owner. That being the player. mobj->angle = angle; mobj->velx = FixedMul(speed, finecosine[angle>>ANGLETOFINESHIFT]); mobj->vely = FixedMul(speed, finesine[angle>>ANGLETOFINESHIFT]); mobj->velz = vspeed; // Set the missile's speed to reflect the speed it was spawned at. if (mobj->flags & MF_MISSILE) { mobj->Speed = fixed_t (sqrt (double(mobj->velx)*mobj->velx + double(mobj->vely)*mobj->vely + double(mobj->velz)*mobj->velz)); } // Hugger missiles don't have any vertical velocity if (mobj->flags3 & (MF3_FLOORHUGGER|MF3_CEILINGHUGGER)) { mobj->velz = 0; } if (mobj->flags & MF_SPECIAL) { mobj->flags |= MF_DROPPED; } if (mobj->flags & MF_MISSILE) { if (P_CheckMissileSpawn (mobj)) { rtn = true; } } else if (!P_TestMobjLocation (mobj)) { // If this is a monster, subtract it from the total monster // count, because it already added to it during spawning. mobj->ClearCounters(); mobj->Destroy (); } else { // It spawned fine. rtn = 1; } } } while (dest != 0 && (targ = tit.Next())); } spot = iterator.Next(); } return rtn != 0; }
int FNodeBuilder::CloseSubsector (TArray<seg_t> &segs, int subsector, vertex_t *outVerts) { FPrivSeg *seg, *prev; angle_t prevAngle; double accumx, accumy; fixed_t midx, midy; int firstVert; DWORD first, max, count, i, j; bool diffplanes; int firstplane; first = Subsectors[subsector].firstline; max = first + Subsectors[subsector].numlines; count = 0; accumx = accumy = 0.0; diffplanes = false; firstplane = Segs[SegList[first].SegNum].planenum; // Calculate the midpoint of the subsector and also check for degenerate subsectors. // A subsector is degenerate if it exists in only one dimension, which can be // detected when all the segs lie in the same plane. This can happen if you have // outward-facing lines in the void that don't point toward any sector. (Some of the // polyobjects in Hexen are constructed like this.) for (i = first; i < max; ++i) { seg = &Segs[SegList[i].SegNum]; accumx += double(Vertices[seg->v1].x) + double(Vertices[seg->v2].x); accumy += double(Vertices[seg->v1].y) + double(Vertices[seg->v2].y); if (firstplane != seg->planenum) { diffplanes = true; } } midx = fixed_t(accumx / (max - first) / 2); midy = fixed_t(accumy / (max - first) / 2); seg = &Segs[SegList[first].SegNum]; prevAngle = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); seg->storedseg = PushGLSeg (segs, seg, outVerts); count = 1; prev = seg; firstVert = seg->v1; if (diffplanes) { // A well-behaved subsector. Output the segs sorted by the angle formed by connecting // the subsector's center to their first vertex. for (i = first + 1; i < max; ++i) { angle_t bestdiff = ANGLE_MAX; FPrivSeg *bestseg = NULL; DWORD bestj = DWORD_MAX; j = first; do { seg = &Segs[SegList[j].SegNum]; angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy); angle_t diff = prevAngle - ang; if (seg->v1 == prev->v2) { bestdiff = diff; bestseg = seg; bestj = j; break; } if (diff < bestdiff && diff > 0) { bestdiff = diff; bestseg = seg; bestj = j; } } while (++j < max); // Is a NULL bestseg actually okay? if (bestseg != NULL) { seg = bestseg; } if (prev->v2 != seg->v1) { // Add a new miniseg to connect the two segs PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[seg->v1]); count++; } prevAngle -= bestdiff; seg->storedseg = PushGLSeg (segs, seg, outVerts); count++; prev = seg; if (seg->v2 == firstVert) { prev = seg; break; } } } else { // A degenerate subsector. These are handled in three stages: // Stage 1. Proceed in the same direction as the start seg until we // hit the seg furthest from it. // Stage 2. Reverse direction and proceed until we hit the seg // furthest from the start seg. // Stage 3. Reverse direction again and insert segs until we get // to the start seg. // A dot product serves to determine distance from the start seg. // Stage 1. Go forward. count += OutputDegenerateSubsector (segs, subsector, true, 0, prev, outVerts); // Stage 2. Go backward. count += OutputDegenerateSubsector (segs, subsector, false, DBL_MAX, prev, outVerts); // Stage 3. Go forward again. count += OutputDegenerateSubsector (segs, subsector, true, -DBL_MAX, prev, outVerts); } if (prev->v2 != firstVert) { PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[firstVert]); count++; } return count; }
int Popup::getAlpha(int maxAlpha) { double a = (double) alpha / (double) FRACUNIT; double b = (double) maxAlpha / (double) FRACUNIT; return fixed_t((a * b) * FRACUNIT); }
//SBarInfo Script Reader void SBarInfo::ParseSBarInfo(int lump) { gameType = gameinfo.gametype; bool baseSet = false; FScanner sc(lump); sc.SetCMode(true); while(sc.CheckToken(TK_Identifier) || sc.CheckToken(TK_Include)) { if(sc.TokenType == TK_Include) { sc.MustGetToken(TK_StringConst); int lump = Wads.CheckNumForFullName(sc.String, true); if (lump == -1) sc.ScriptError("Lump '%s' not found", sc.String); ParseSBarInfo(lump); continue; } switch(sc.MustMatchString(SBarInfoTopLevel)) { case SBARINFO_BASE: baseSet = true; if(!sc.CheckToken(TK_None)) sc.MustGetToken(TK_Identifier); if(sc.Compare("Doom")) { int lump = Wads.CheckNumForFullName("sbarinfo/doom.txt", true); if(lump == -1) sc.ScriptError("Standard Doom Status Bar not found."); ParseSBarInfo(lump); } else if(sc.Compare("Heretic")) gameType = GAME_Heretic; else if(sc.Compare("Hexen")) gameType = GAME_Hexen; else if(sc.Compare("Strife")) gameType = GAME_Strife; else if(sc.Compare("None")) gameType = GAME_Any; else sc.ScriptError("Bad game name: %s", sc.String); sc.MustGetToken(';'); break; case SBARINFO_HEIGHT: sc.MustGetToken(TK_IntConst); this->height = sc.Number; sc.MustGetToken(';'); break; case SBARINFO_INTERPOLATEHEALTH: //mimics heretics interpolated health values. if(sc.CheckToken(TK_True)) { interpolateHealth = true; } else { sc.MustGetToken(TK_False); interpolateHealth = false; } if(sc.CheckToken(',')) //speed param { sc.MustGetToken(TK_IntConst); this->interpolationSpeed = sc.Number; } sc.MustGetToken(';'); break; case SBARINFO_INTERPOLATEARMOR: //Since interpolatehealth is such a popular command if(sc.CheckToken(TK_True)) { interpolateArmor = true; } else { sc.MustGetToken(TK_False); interpolateArmor = false; } if(sc.CheckToken(',')) //speed { sc.MustGetToken(TK_IntConst); this->armorInterpolationSpeed = sc.Number; } sc.MustGetToken(';'); break; case SBARINFO_COMPLETEBORDER: //draws the border instead of an HOM if(sc.CheckToken(TK_True)) { completeBorder = true; } else { sc.MustGetToken(TK_False); completeBorder = false; } sc.MustGetToken(';'); break; case SBARINFO_MONOSPACEFONTS: if(sc.CheckToken(TK_True)) { sc.MustGetToken(','); sc.MustGetToken(TK_StringConst); spacingCharacter = sc.String[0]; } else { sc.MustGetToken(TK_False); spacingCharacter = '\0'; sc.MustGetToken(','); sc.MustGetToken(TK_StringConst); //Don't tell anyone we're just ignoring this ;) } sc.MustGetToken(';'); break; case SBARINFO_LOWERHEALTHCAP: if(sc.CheckToken(TK_False)) { lowerHealthCap = false; } else { sc.MustGetToken(TK_True); lowerHealthCap = true; } sc.MustGetToken(';'); break; case SBARINFO_STATUSBAR: { if(!baseSet) //If the user didn't explicitly define a base, do so now. gameType = GAME_Any; int barNum = 0; if(!sc.CheckToken(TK_None)) { sc.MustGetToken(TK_Identifier); barNum = sc.MustMatchString(StatusBars); } this->huds[barNum] = SBarInfoBlock(); if(sc.CheckToken(',')) { while(sc.CheckToken(TK_Identifier)) { if(sc.Compare("forcescaled")) { this->huds[barNum].forceScaled = true; } else if(sc.Compare("fullscreenoffsets")) { this->huds[barNum].fullScreenOffsets = true; } else { sc.ScriptError("Unkown flag '%s'.", sc.String); } if(!sc.CheckToken('|') && !sc.CheckToken(',')) goto FinishStatusBar; //No more args so we must skip over anything else and go to the end. } sc.MustGetToken(TK_FloatConst); this->huds[barNum].alpha = fixed_t(FRACUNIT * sc.Float); } FinishStatusBar: sc.MustGetToken('{'); if(barNum == STBAR_AUTOMAP) { automapbar = true; } ParseSBarInfoBlock(sc, this->huds[barNum]); break; } case SBARINFO_MUGSHOT: { sc.MustGetToken(TK_StringConst); FMugShotState state(sc.String); if(sc.CheckToken(',')) //first loop must be a comma { do { sc.MustGetToken(TK_Identifier); if(sc.Compare("health")) state.bUsesLevels = true; else if(sc.Compare("health2")) state.bUsesLevels = state.bHealth2 = true; else if(sc.Compare("healthspecial")) state.bUsesLevels = state.bHealthSpecial = true; else if(sc.Compare("directional")) state.bDirectional = true; else sc.ScriptError("Unknown MugShot state flag '%s'.", sc.String); } while(sc.CheckToken(',') || sc.CheckToken('|')); } ParseMugShotBlock(sc, state); int index; if((index = FindMugShotStateIndex(state.State)) != -1) //We already had this state, remove the old one. { MugShotStates.Delete(index); } MugShotStates.Push(state); break; } case SBARINFO_CREATEPOPUP: { int pop = 0; sc.MustGetToken(TK_Identifier); if(sc.Compare("log")) pop = 0; else if(sc.Compare("keys")) pop = 1; else if(sc.Compare("status")) pop = 2; else sc.ScriptError("Unkown popup: '%s'", sc.String); Popup &popup = popups[pop]; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); popup.width = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); popup.height = sc.Number; sc.MustGetToken(','); if(!sc.CheckToken(TK_None)) { sc.MustGetToken(TK_Identifier); if(sc.Compare("slideinbottom")) { popup.transition = TRANSITION_SLIDEINBOTTOM; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); popup.speed = sc.Number; } else if(sc.Compare("fade")) { popup.transition = TRANSITION_FADE; sc.MustGetToken(','); sc.MustGetToken(TK_FloatConst); popup.speed = fixed_t(FRACUNIT * (1.0 / (35.0 * sc.Float))); sc.MustGetToken(','); sc.MustGetToken(TK_FloatConst); popup.speed2 = fixed_t(FRACUNIT * (1.0 / (35.0 * sc.Float))); } else sc.ScriptError("Unkown transition type: '%s'", sc.String); } popup.init(); sc.MustGetToken(';'); break; } } } }
/* #include "actor.h" #include "gi.h" #include "m_random.h" #include "s_sound.h" #include "d_player.h" #include "a_action.h" #include "p_local.h" #include "a_action.h" #include "p_pspr.h" #include "gstrings.h" #include "a_hexenglobal.h" #include "thingdef/thingdef.h" */ const fixed_t FLAMESPEED = fixed_t(0.45*FRACUNIT); const fixed_t CFLAMERANGE = 12*64*FRACUNIT; const fixed_t FLAMEROTSPEED = 2*FRACUNIT; static FRandom pr_missile ("CFlameMissile"); void A_CFlameAttack (AActor *); void A_CFlameRotate (AActor *); void A_CFlamePuff (AActor *); void A_CFlameMissile (AActor *); // Flame Missile ------------------------------------------------------------ class ACFlameMissile : public AFastProjectile { DECLARE_CLASS (ACFlameMissile, AFastProjectile)